summaryrefslogtreecommitdiff
path: root/usr/src/lib/libfakekernel/common/ksocket.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libfakekernel/common/ksocket.c')
-rw-r--r--usr/src/lib/libfakekernel/common/ksocket.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/usr/src/lib/libfakekernel/common/ksocket.c b/usr/src/lib/libfakekernel/common/ksocket.c
new file mode 100644
index 0000000000..53bcf87576
--- /dev/null
+++ b/usr/src/lib/libfakekernel/common/ksocket.c
@@ -0,0 +1,535 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/cred.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/ksocket.h>
+#include <sys/debug.h>
+#include <sys/kmem.h>
+#include <unistd.h>
+#include <errno.h>
+#include <umem.h>
+
+#define _KSOCKET_MAGIC 0xabcdef09
+
+#define KSOCKET_VALID(ks) (ks->kso_magic == _KSOCKET_MAGIC)
+#define KSTOSO(ks) (ks->kso_fd)
+
+#ifndef SS_CLOSING
+#define SS_CLOSING 0x00010000
+#endif
+
+/*
+ * NB: you can't cast this into a sonode like you can with a normal
+ * ksocket_t, but no correct code should ever do that anyway.
+ * The ksocket_t type is opaque to prevent exactly that.
+ */
+struct __ksocket {
+ uint32_t kso_magic;
+ uint32_t kso_count;
+ uint32_t kso_state;
+ int kso_fd;
+ kmutex_t kso_lock;
+ kcondvar_t kso_closing_cv;
+};
+
+static umem_cache_t *ksocket_cache = NULL;
+
+/*ARGSUSED*/
+static int
+_ksocket_ctor(void *buf, void *arg, int flags)
+{
+ ksocket_t sock = buf;
+
+ bzero(sock, sizeof (*sock));
+ mutex_init(&sock->kso_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&sock->kso_closing_cv, NULL, CV_DEFAULT, NULL);
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+_ksocket_dtor(void *buf, void *arg)
+{
+ ksocket_t sock = buf;
+
+ mutex_destroy(&sock->kso_lock);
+ cv_destroy(&sock->kso_closing_cv);
+}
+
+#pragma init(_ksocket_init)
+int
+_ksocket_init(void)
+{
+ ksocket_cache = umem_cache_create("ksocket",
+ sizeof (struct __ksocket), 0,
+ _ksocket_ctor, _ksocket_dtor, NULL, NULL, NULL, 0);
+ VERIFY(ksocket_cache != NULL);
+ return (0);
+}
+
+#pragma fini(_ksocket_fini)
+int
+_ksocket_fini(void)
+{
+ umem_cache_destroy(ksocket_cache);
+ return (0);
+}
+
+static ksocket_t
+_ksocket_create(int fd)
+{
+ ksocket_t ks;
+
+ ks = umem_cache_alloc(ksocket_cache, 0);
+ VERIFY(ks != NULL);
+ ks->kso_magic = _KSOCKET_MAGIC;
+ ks->kso_count = 1;
+ ks->kso_fd = fd;
+ return (ks);
+}
+
+static void
+_ksocket_destroy(ksocket_t ks)
+{
+ ASSERT(ks->kso_count == 1);
+ umem_cache_free(ksocket_cache, ks);
+}
+
+int
+ksocket_socket(ksocket_t *ksp, int domain, int type, int protocol, int flags,
+ struct cred *cr)
+{
+ int fd;
+ ksocket_t ks;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ ASSERT(flags == KSOCKET_SLEEP || flags == KSOCKET_NOSLEEP);
+
+ fd = socket(domain, type, protocol);
+ if (fd < 0) {
+ *ksp = NULL;
+ return (errno);
+ }
+
+ ks = _ksocket_create(fd);
+ *ksp = ks;
+ return (0);
+}
+
+/*
+ * This is marked NODIRECT so the main program linking with this library
+ * can provide its own "bind helper" function. See: fksmbd_ksock.c
+ */
+/* ARGSUSED */
+int
+ksocket_bind_helper(int fd, struct sockaddr *addr, uint_t addrlen)
+{
+ return (EACCES);
+}
+
+int
+ksocket_bind(ksocket_t ks, struct sockaddr *addr, socklen_t addrlen,
+ struct cred *cr)
+{
+ int err = 0;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (bind(KSTOSO(ks), addr, addrlen) != 0)
+ err = errno;
+
+ if (err == EACCES) {
+ err = ksocket_bind_helper(KSTOSO(ks), addr, addrlen);
+ }
+
+ return (err);
+}
+
+int
+ksocket_listen(ksocket_t ks, int backlog, struct cred *cr)
+{
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (listen(KSTOSO(ks), backlog) != 0)
+ return (errno);
+
+ return (0);
+}
+
+int
+ksocket_accept(ksocket_t ks, struct sockaddr *addr,
+ socklen_t *addrlenp, ksocket_t *nks, struct cred *cr)
+{
+ int fd;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ *nks = NULL;
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (addr != NULL && addrlenp == NULL)
+ return (EFAULT);
+
+ fd = accept(KSTOSO(ks), addr, addrlenp);
+ if (fd < 0)
+ return (errno);
+
+ *nks = _ksocket_create(fd);
+
+ return (0);
+}
+
+int
+ksocket_connect(ksocket_t ks, struct sockaddr *addr, socklen_t addrlen,
+ struct cred *cr)
+{
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (connect(KSTOSO(ks), addr, addrlen) != 0)
+ return (errno);
+
+ return (0);
+}
+
+int
+ksocket_send(ksocket_t ks, void *msg, size_t msglen, int flags,
+ size_t *sent, struct cred *cr)
+{
+ ssize_t error;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks)) {
+ if (sent != NULL)
+ *sent = 0;
+ return (ENOTSOCK);
+ }
+
+ error = send(KSTOSO(ks), msg, msglen, flags);
+ if (error < 0) {
+ if (sent != NULL)
+ *sent = 0;
+ return (errno);
+ }
+
+ if (sent != NULL)
+ *sent = (size_t)error;
+ return (0);
+}
+
+int
+ksocket_sendto(ksocket_t ks, void *msg, size_t msglen, int flags,
+ struct sockaddr *name, socklen_t namelen, size_t *sent, struct cred *cr)
+{
+ ssize_t error;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks)) {
+ if (sent != NULL)
+ *sent = 0;
+ return (ENOTSOCK);
+ }
+
+ error = sendto(KSTOSO(ks), msg, msglen, flags, name, namelen);
+ if (error < 0) {
+ if (sent != NULL)
+ *sent = 0;
+ return (errno);
+ }
+
+ if (sent != NULL)
+ *sent = (size_t)error;
+ return (0);
+}
+
+int
+ksocket_sendmsg(ksocket_t ks, struct nmsghdr *msg, int flags,
+ size_t *sent, struct cred *cr)
+{
+ ssize_t error;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks)) {
+ if (sent != NULL)
+ *sent = 0;
+ return (ENOTSOCK);
+ }
+
+ error = sendmsg(KSTOSO(ks), msg, flags);
+ if (error < 0) {
+ if (sent != NULL)
+ *sent = 0;
+ return (errno);
+ }
+
+ if (sent != NULL)
+ *sent = (size_t)error;
+ return (0);
+}
+
+int
+ksocket_recv(ksocket_t ks, void *msg, size_t msglen, int flags,
+ size_t *recvd, struct cred *cr)
+{
+ ssize_t error;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks)) {
+ if (recvd != NULL)
+ *recvd = 0;
+ return (ENOTSOCK);
+ }
+
+ error = recv(KSTOSO(ks), msg, msglen, flags);
+ if (error < 0) {
+ if (recvd != NULL)
+ *recvd = 0;
+ return (errno);
+ }
+
+ if (recvd != NULL)
+ *recvd = (size_t)error;
+ return (0);
+}
+
+int
+ksocket_recvfrom(ksocket_t ks, void *msg, size_t msglen, int flags,
+ struct sockaddr *name, socklen_t *namelen, size_t *recvd, struct cred *cr)
+{
+ ssize_t error;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks)) {
+ if (recvd != NULL)
+ *recvd = 0;
+ return (ENOTSOCK);
+ }
+
+ error = recvfrom(KSTOSO(ks), msg, msglen, flags, name, namelen);
+ if (error != 0) {
+ if (recvd != NULL)
+ *recvd = 0;
+ return (errno);
+ }
+
+ if (recvd != NULL)
+ *recvd = (ssize_t)error;
+ return (0);
+}
+
+int
+ksocket_recvmsg(ksocket_t ks, struct nmsghdr *msg, int flags, size_t *recvd,
+ struct cred *cr)
+{
+ ssize_t error;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks)) {
+ if (recvd != NULL)
+ *recvd = 0;
+ return (ENOTSOCK);
+ }
+
+ error = recvmsg(KSTOSO(ks), msg, flags);
+ if (error < 0) {
+ if (recvd != NULL)
+ *recvd = 0;
+ return (errno);
+ }
+
+ if (recvd != NULL)
+ *recvd = (size_t)error;
+ return (0);
+}
+
+int
+ksocket_shutdown(ksocket_t ks, int how, struct cred *cr)
+{
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (shutdown(KSTOSO(ks), how) != 0)
+ return (errno);
+
+ return (0);
+}
+
+int
+ksocket_close(ksocket_t ks, struct cred *cr)
+{
+ int fd;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ mutex_enter(&ks->kso_lock);
+
+ if (!KSOCKET_VALID(ks)) {
+ mutex_exit(&ks->kso_lock);
+ return (ENOTSOCK);
+ }
+
+ ks->kso_state |= SS_CLOSING;
+
+ /*
+ * The real ksocket wakes up everything.
+ * It seems the only way we can do that
+ * is to go ahead and close the FD.
+ */
+ fd = ks->kso_fd;
+ ks->kso_fd = -1;
+ (void) close(fd);
+
+ while (ks->kso_count > 1)
+ cv_wait(&ks->kso_closing_cv, &ks->kso_lock);
+
+ mutex_exit(&ks->kso_lock);
+ _ksocket_destroy(ks);
+
+ return (0);
+}
+
+int
+ksocket_getsockname(ksocket_t ks, struct sockaddr *addr, socklen_t *addrlen,
+ struct cred *cr)
+{
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (addrlen == NULL || (addr == NULL && *addrlen != 0))
+ return (EFAULT);
+
+ if (getsockname(KSTOSO(ks), addr, addrlen) != 0)
+ return (errno);
+
+ return (0);
+}
+
+int
+ksocket_getpeername(ksocket_t ks, struct sockaddr *addr, socklen_t *addrlen,
+ struct cred *cr)
+{
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (addrlen == NULL || (addr == NULL && *addrlen != 0))
+ return (EFAULT);
+
+ if (getpeername(KSTOSO(ks), addr, addrlen) != 0)
+ return (errno);
+
+ return (0);
+}
+
+int
+ksocket_setsockopt(ksocket_t ks, int level, int optname, const void *optval,
+ int optlen, struct cred *cr)
+{
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+
+ if (optval == NULL)
+ optlen = 0;
+
+ if (setsockopt(KSTOSO(ks), level, optname, optval, optlen) != 0)
+ return (errno);
+
+ return (0);
+}
+
+void
+ksocket_hold(ksocket_t ks)
+{
+ if (!mutex_owned(&ks->kso_lock)) {
+ mutex_enter(&ks->kso_lock);
+ ks->kso_count++;
+ mutex_exit(&ks->kso_lock);
+ } else
+ ks->kso_count++;
+}
+
+void
+ksocket_rele(ksocket_t ks)
+{
+ /*
+ * When so_count equals 1 means no thread working on this ksocket
+ */
+ VERIFY3U(ks->kso_count, >, 1);
+
+ if (!mutex_owned(&ks->kso_lock)) {
+ mutex_enter(&ks->kso_lock);
+ if (--ks->kso_count == 1)
+ cv_signal(&ks->kso_closing_cv);
+ mutex_exit(&ks->kso_lock);
+ } else {
+ if (--ks->kso_count == 1)
+ cv_signal(&ks->kso_closing_cv);
+ }
+}