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 5*e11c3f44Smeem * Common Development and Distribution License (the "License"). 6*e11c3f44Smeem * 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 20*e11c3f44Smeem * 21*e11c3f44Smeem * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 227c478bd9Sstevel@tonic-gate * Use is subject to license terms. 237c478bd9Sstevel@tonic-gate */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate /* 267c478bd9Sstevel@tonic-gate * Low-level interfaces for communicating with in.mpathd(1M). 277c478bd9Sstevel@tonic-gate * 287c478bd9Sstevel@tonic-gate * These routines are not intended for use outside of libipmp. 297c478bd9Sstevel@tonic-gate */ 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate #include <alloca.h> 327c478bd9Sstevel@tonic-gate #include <assert.h> 337c478bd9Sstevel@tonic-gate #include <errno.h> 347c478bd9Sstevel@tonic-gate #include <fcntl.h> 357c478bd9Sstevel@tonic-gate #include <stdlib.h> 367c478bd9Sstevel@tonic-gate #include <string.h> 377c478bd9Sstevel@tonic-gate #include <unistd.h> 387c478bd9Sstevel@tonic-gate #include <poll.h> 397c478bd9Sstevel@tonic-gate #include <sys/socket.h> 407c478bd9Sstevel@tonic-gate #include <netinet/in.h> 417c478bd9Sstevel@tonic-gate #include <netinet/tcp.h> 427c478bd9Sstevel@tonic-gate #include <sys/types.h> 437c478bd9Sstevel@tonic-gate 447c478bd9Sstevel@tonic-gate #include "ipmp.h" 457c478bd9Sstevel@tonic-gate #include "ipmp_mpathd.h" 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate /* 487c478bd9Sstevel@tonic-gate * Connect to the multipathing daemon. Returns an IPMP error code; upon 497c478bd9Sstevel@tonic-gate * success, `fdp' points to the newly opened socket. 507c478bd9Sstevel@tonic-gate */ 517c478bd9Sstevel@tonic-gate int 527c478bd9Sstevel@tonic-gate ipmp_connect(int *fdp) 537c478bd9Sstevel@tonic-gate { 547c478bd9Sstevel@tonic-gate int fd; 557c478bd9Sstevel@tonic-gate int error; 567c478bd9Sstevel@tonic-gate int on = 1; 577c478bd9Sstevel@tonic-gate int flags; 587c478bd9Sstevel@tonic-gate struct sockaddr_in sin; 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate fd = socket(AF_INET, SOCK_STREAM, 0); 617c478bd9Sstevel@tonic-gate if (fd == -1) 627c478bd9Sstevel@tonic-gate return (IPMP_FAILURE); 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate /* 65*e11c3f44Smeem * If we have sufficient privilege, enable TCP_ANONPRIVBIND so the 66*e11c3f44Smeem * kernel will choose a privileged source port (since in.mpathd only 67*e11c3f44Smeem * accepts requests on loopback, this is sufficient for security). 68*e11c3f44Smeem * If not, drive on since MI_QUERY and MI_PING commands are allowed 69*e11c3f44Smeem * from non-privileged ports. 707c478bd9Sstevel@tonic-gate */ 71*e11c3f44Smeem (void) setsockopt(fd, IPPROTO_TCP, TCP_ANONPRIVBIND, &on, sizeof (on)); 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate /* 74*e11c3f44Smeem * Bind to a port chosen by the kernel. 757c478bd9Sstevel@tonic-gate */ 767c478bd9Sstevel@tonic-gate (void) memset(&sin, 0, sizeof (struct sockaddr_in)); 777c478bd9Sstevel@tonic-gate sin.sin_port = htons(0); 787c478bd9Sstevel@tonic-gate sin.sin_family = AF_INET; 797c478bd9Sstevel@tonic-gate sin.sin_addr.s_addr = htonl(INADDR_ANY); 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate if (bind(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) 827c478bd9Sstevel@tonic-gate goto fail; 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate /* 857c478bd9Sstevel@tonic-gate * Attempt to connect to in.mpathd. 867c478bd9Sstevel@tonic-gate */ 877c478bd9Sstevel@tonic-gate sin.sin_port = htons(MPATHD_PORT); 887c478bd9Sstevel@tonic-gate sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate if (connect(fd, (struct sockaddr *)&sin, sizeof (sin)) == -1) { 917c478bd9Sstevel@tonic-gate if (errno == ECONNREFUSED) { 927c478bd9Sstevel@tonic-gate (void) close(fd); 937c478bd9Sstevel@tonic-gate return (IPMP_ENOMPATHD); 947c478bd9Sstevel@tonic-gate } 957c478bd9Sstevel@tonic-gate goto fail; 967c478bd9Sstevel@tonic-gate } 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate /* 997c478bd9Sstevel@tonic-gate * Kick the socket into nonblocking mode. 1007c478bd9Sstevel@tonic-gate */ 1017c478bd9Sstevel@tonic-gate flags = fcntl(fd, F_GETFL, 0); 1027c478bd9Sstevel@tonic-gate if (flags != -1) 1037c478bd9Sstevel@tonic-gate (void) fcntl(fd, F_SETFL, flags | O_NONBLOCK); 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate *fdp = fd; 1067c478bd9Sstevel@tonic-gate return (IPMP_SUCCESS); 1077c478bd9Sstevel@tonic-gate fail: 1087c478bd9Sstevel@tonic-gate error = errno; 1097c478bd9Sstevel@tonic-gate (void) close(fd); 1107c478bd9Sstevel@tonic-gate errno = error; 1117c478bd9Sstevel@tonic-gate return (IPMP_FAILURE); 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate /* 1157c478bd9Sstevel@tonic-gate * Read the TLV triplet from descriptor `fd' and store its type, length and 1167c478bd9Sstevel@tonic-gate * value in `*typep', `*lenp', and `*valuep' respectively, before the current 1177c478bd9Sstevel@tonic-gate * time becomes `endtp'. The buffer pointed to by `*valuep' will be 1187c478bd9Sstevel@tonic-gate * dynamically allocated. Returns an IPMP error code. 1197c478bd9Sstevel@tonic-gate */ 1207c478bd9Sstevel@tonic-gate int 1217c478bd9Sstevel@tonic-gate ipmp_readtlv(int fd, ipmp_infotype_t *typep, size_t *lenp, void **valuep, 1227c478bd9Sstevel@tonic-gate const struct timeval *endtp) 1237c478bd9Sstevel@tonic-gate { 1247c478bd9Sstevel@tonic-gate int retval; 1257c478bd9Sstevel@tonic-gate void *value; 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate retval = ipmp_read(fd, typep, sizeof (*typep), endtp); 1287c478bd9Sstevel@tonic-gate if (retval != IPMP_SUCCESS) 1297c478bd9Sstevel@tonic-gate return (retval); 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate retval = ipmp_read(fd, lenp, sizeof (*lenp), endtp); 1327c478bd9Sstevel@tonic-gate if (retval != IPMP_SUCCESS) 1337c478bd9Sstevel@tonic-gate return (retval); 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate value = malloc(*lenp); 1367c478bd9Sstevel@tonic-gate if (value == NULL) { 1377c478bd9Sstevel@tonic-gate /* 1387c478bd9Sstevel@tonic-gate * Even though we cannot allocate space for the value, we 1397c478bd9Sstevel@tonic-gate * still slurp it off so the input stream doesn't get left 1407c478bd9Sstevel@tonic-gate * in a weird place. 1417c478bd9Sstevel@tonic-gate */ 1427c478bd9Sstevel@tonic-gate value = alloca(*lenp); 1437c478bd9Sstevel@tonic-gate (void) ipmp_read(fd, value, *lenp, endtp); 1447c478bd9Sstevel@tonic-gate return (IPMP_ENOMEM); 1457c478bd9Sstevel@tonic-gate } 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate retval = ipmp_read(fd, value, *lenp, endtp); 1487c478bd9Sstevel@tonic-gate if (retval != IPMP_SUCCESS) { 1497c478bd9Sstevel@tonic-gate free(value); 1507c478bd9Sstevel@tonic-gate return (retval); 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate *valuep = value; 1547c478bd9Sstevel@tonic-gate return (IPMP_SUCCESS); 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate /* 1587c478bd9Sstevel@tonic-gate * Write `buflen' bytes from `buffer' to open file `fd'. Returns IPMP_SUCCESS 1597c478bd9Sstevel@tonic-gate * if all requested bytes were written, or an error code if not. 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate int 1627c478bd9Sstevel@tonic-gate ipmp_write(int fd, const void *buffer, size_t buflen) 1637c478bd9Sstevel@tonic-gate { 1647c478bd9Sstevel@tonic-gate size_t nwritten; 1657c478bd9Sstevel@tonic-gate ssize_t nbytes; 1667c478bd9Sstevel@tonic-gate const char *buf = buffer; 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate for (nwritten = 0; nwritten < buflen; nwritten += nbytes) { 1697c478bd9Sstevel@tonic-gate nbytes = write(fd, &buf[nwritten], buflen - nwritten); 1707c478bd9Sstevel@tonic-gate if (nbytes == -1) 1717c478bd9Sstevel@tonic-gate return (IPMP_FAILURE); 1727c478bd9Sstevel@tonic-gate if (nbytes == 0) { 1737c478bd9Sstevel@tonic-gate errno = EIO; 1747c478bd9Sstevel@tonic-gate return (IPMP_FAILURE); 1757c478bd9Sstevel@tonic-gate } 1767c478bd9Sstevel@tonic-gate } 1777c478bd9Sstevel@tonic-gate 1787c478bd9Sstevel@tonic-gate assert(nwritten == buflen); 1797c478bd9Sstevel@tonic-gate return (IPMP_SUCCESS); 1807c478bd9Sstevel@tonic-gate } 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate /* 1837c478bd9Sstevel@tonic-gate * Write the TLV triplet named by `type', `len' and `value' to file descriptor 1847c478bd9Sstevel@tonic-gate * `fd'. Returns an IPMP error code. 1857c478bd9Sstevel@tonic-gate */ 1867c478bd9Sstevel@tonic-gate int 1877c478bd9Sstevel@tonic-gate ipmp_writetlv(int fd, ipmp_infotype_t type, size_t len, void *value) 1887c478bd9Sstevel@tonic-gate { 1897c478bd9Sstevel@tonic-gate int retval; 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate retval = ipmp_write(fd, &type, sizeof (type)); 1927c478bd9Sstevel@tonic-gate if (retval != IPMP_SUCCESS) 1937c478bd9Sstevel@tonic-gate return (retval); 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gate retval = ipmp_write(fd, &len, sizeof (len)); 1967c478bd9Sstevel@tonic-gate if (retval != IPMP_SUCCESS) 1977c478bd9Sstevel@tonic-gate return (retval); 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate return (ipmp_write(fd, value, len)); 2007c478bd9Sstevel@tonic-gate } 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate /* 2037c478bd9Sstevel@tonic-gate * Attempt to read `buflen' worth of bytes from `fd' into the buffer pointed 2047c478bd9Sstevel@tonic-gate * to by `buf' before the current time becomes `endtp'; a `endtp' of NULL 2057c478bd9Sstevel@tonic-gate * means forever. Returns an IPMP error code. 2067c478bd9Sstevel@tonic-gate */ 2077c478bd9Sstevel@tonic-gate int 2087c478bd9Sstevel@tonic-gate ipmp_read(int fd, void *buffer, size_t buflen, const struct timeval *endtp) 2097c478bd9Sstevel@tonic-gate { 2107c478bd9Sstevel@tonic-gate int retval; 2117c478bd9Sstevel@tonic-gate int timeleft = -1; 2127c478bd9Sstevel@tonic-gate struct timeval curtime; 2137c478bd9Sstevel@tonic-gate ssize_t nbytes = 0; /* total bytes processed */ 2147c478bd9Sstevel@tonic-gate ssize_t prbytes; /* per-round bytes processed */ 2157c478bd9Sstevel@tonic-gate struct pollfd pfd; 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate while (nbytes < buflen) { 2187c478bd9Sstevel@tonic-gate /* 2197c478bd9Sstevel@tonic-gate * If a timeout was specified, then compute the amount of time 2207c478bd9Sstevel@tonic-gate * left before timing out. 2217c478bd9Sstevel@tonic-gate */ 2227c478bd9Sstevel@tonic-gate if (endtp != NULL) { 2237c478bd9Sstevel@tonic-gate if (gettimeofday(&curtime, NULL) == -1) 2247c478bd9Sstevel@tonic-gate break; 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate timeleft = (endtp->tv_sec - curtime.tv_sec) * MILLISEC; 2277c478bd9Sstevel@tonic-gate timeleft += (endtp->tv_usec - curtime.tv_usec) / 1000; 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate /* 2307c478bd9Sstevel@tonic-gate * If we should've already timed out, then just 2317c478bd9Sstevel@tonic-gate * have poll() return immediately. 2327c478bd9Sstevel@tonic-gate */ 2337c478bd9Sstevel@tonic-gate if (timeleft < 0) 2347c478bd9Sstevel@tonic-gate timeleft = 0; 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate 2377c478bd9Sstevel@tonic-gate pfd.fd = fd; 2387c478bd9Sstevel@tonic-gate pfd.events = POLLIN; 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate /* 2417c478bd9Sstevel@tonic-gate * Wait for data to come in or for the timeout to fire. 2427c478bd9Sstevel@tonic-gate */ 2437c478bd9Sstevel@tonic-gate retval = poll(&pfd, 1, timeleft); 2447c478bd9Sstevel@tonic-gate if (retval <= 0) { 2457c478bd9Sstevel@tonic-gate if (retval == 0) 2467c478bd9Sstevel@tonic-gate errno = ETIME; 2477c478bd9Sstevel@tonic-gate break; 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate /* 2517c478bd9Sstevel@tonic-gate * Descriptor is ready; have at it. 2527c478bd9Sstevel@tonic-gate */ 2537c478bd9Sstevel@tonic-gate prbytes = read(fd, (caddr_t)buffer + nbytes, buflen - nbytes); 2547c478bd9Sstevel@tonic-gate if (prbytes <= 0) { 2557c478bd9Sstevel@tonic-gate if (prbytes == -1 && errno == EINTR) 2567c478bd9Sstevel@tonic-gate continue; 2577c478bd9Sstevel@tonic-gate break; 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate nbytes += prbytes; 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate return (nbytes == buflen ? IPMP_SUCCESS : IPMP_FAILURE); 2637c478bd9Sstevel@tonic-gate } 264