2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A/*
2N/A * Portions of this source code were derived from Berkeley 4.3 BSD
2N/A * under license from the Regents of the University of California.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <sys/param.h>
2N/A#include <sys/types.h>
2N/A#include <rpc/types.h>
2N/A#include <netinet/in.h>
2N/A#include <rpc/auth.h>
2N/A#include <rpc/clnt.h>
2N/A#include <sys/tiuser.h>
2N/A#include <sys/t_kuser.h>
2N/A#include <rpc/svc.h>
2N/A#include <rpc/xdr.h>
2N/A#include <sys/file.h>
2N/A#include <sys/user.h>
2N/A#include <sys/proc.h>
2N/A#include <sys/vnode.h>
2N/A#include <sys/stream.h>
2N/A#include <sys/tihdr.h>
2N/A#include <sys/fcntl.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/sysmacros.h>
2N/A#include <sys/errno.h>
2N/A#include <sys/cred.h>
2N/A#include <sys/systm.h>
2N/A#include <sys/cmn_err.h>
2N/A
2N/A#define NC_INET "inet"
2N/A
2N/A#define MAX_PRIV (IPPORT_RESERVED-1)
2N/A#define MIN_PRIV (IPPORT_RESERVED/2)
2N/A
2N/Aushort_t clnt_udp_last_used = MIN_PRIV;
2N/Aushort_t clnt_tcp_last_used = MIN_PRIV;
2N/A
2N/A/*
2N/A * PSARC 2003/523 Contract Private Interface
2N/A * clnt_tli_kcreate
2N/A * Changes must be reviewed by Solaris File Sharing
2N/A * Changes must be communicated to contract-2003-523@sun.com
2N/A */
2N/Aint
2N/Aclnt_tli_kcreate(
2N/A struct knetconfig *config,
2N/A struct netbuf *svcaddr, /* Servers address */
2N/A rpcprog_t prog, /* Program number */
2N/A rpcvers_t vers, /* Version number */
2N/A uint_t max_msgsize,
2N/A int retries,
2N/A struct cred *cred,
2N/A CLIENT **ncl)
2N/A{
2N/A CLIENT *cl; /* Client handle */
2N/A int error;
2N/A int family = AF_UNSPEC;
2N/A
2N/A error = 0;
2N/A cl = NULL;
2N/A
2N/A RPCLOG(8, "clnt_tli_kcreate: prog %x", prog);
2N/A RPCLOG(8, ", vers %d", vers);
2N/A RPCLOG(8, ", knc_semantics %d", config->knc_semantics);
2N/A RPCLOG(8, ", knc_protofmly %s", config->knc_protofmly);
2N/A RPCLOG(8, ", knc_proto %s\n", config->knc_proto);
2N/A
2N/A if (config == NULL || config->knc_protofmly == NULL || ncl == NULL) {
2N/A RPCLOG0(1, "clnt_tli_kcreate: bad config or handle\n");
2N/A return (EINVAL);
2N/A }
2N/A
2N/A switch (config->knc_semantics) {
2N/A case NC_TPI_CLTS:
2N/A RPCLOG0(8, "clnt_tli_kcreate: CLTS selected\n");
2N/A error = clnt_clts_kcreate(config, svcaddr, prog, vers,
2N/A retries, cred, &cl);
2N/A if (error != 0) {
2N/A RPCLOG(1,
2N/A "clnt_tli_kcreate: clnt_clts_kcreate failed error %d\n",
2N/A error);
2N/A return (error);
2N/A }
2N/A break;
2N/A
2N/A case NC_TPI_COTS:
2N/A case NC_TPI_COTS_ORD:
2N/A RPCLOG0(8, "clnt_tli_kcreate: COTS selected\n");
2N/A if (strcmp(config->knc_protofmly, NC_INET) == 0)
2N/A family = AF_INET;
2N/A else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
2N/A family = AF_INET6;
2N/A error = clnt_cots_kcreate(config->knc_rdev, svcaddr, family,
2N/A prog, vers, max_msgsize, cred, &cl);
2N/A if (error != 0) {
2N/A RPCLOG(1,
2N/A "clnt_tli_kcreate: clnt_cots_kcreate failed error %d\n",
2N/A error);
2N/A return (error);
2N/A }
2N/A break;
2N/A case NC_TPI_RDMA:
2N/A RPCLOG0(8, "clnt_tli_kcreate: RDMA selected\n");
2N/A /*
2N/A * RDMA doesn't support TSOL. It's better to
2N/A * disallow it here.
2N/A */
2N/A if (is_system_labeled()) {
2N/A RPCLOG0(1, "clnt_tli_kcreate: tsol not supported\n");
2N/A return (EPROTONOSUPPORT);
2N/A }
2N/A
2N/A if (strcmp(config->knc_protofmly, NC_INET) == 0)
2N/A family = AF_INET;
2N/A else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
2N/A family = AF_INET6;
2N/A error = clnt_rdma_kcreate(config->knc_proto,
2N/A (void *)config->knc_rdev, svcaddr, family, prog, vers, cred,
2N/A &cl);
2N/A if (error != 0) {
2N/A RPCLOG(1,
2N/A "clnt_tli_kcreate: clnt_rdma_kcreate failed error %d\n",
2N/A error);
2N/A return (error);
2N/A }
2N/A break;
2N/A default:
2N/A error = EINVAL;
2N/A RPCLOG(1, "clnt_tli_kcreate: Bad service type %d\n",
2N/A config->knc_semantics);
2N/A return (error);
2N/A }
2N/A *ncl = cl;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * "Kinit" a client handle by calling the appropriate cots or clts routine.
2N/A *
2N/A * PSARC 2003/523 Contract Private Interface
2N/A * clnt_tli_kinit
2N/A * Changes must be reviewed by Solaris File Sharing
2N/A * Changes must be communicated to contract-2003-523@sun.com
2N/A */
2N/Aint
2N/Aclnt_tli_kinit(
2N/A CLIENT *h,
2N/A struct knetconfig *config,
2N/A struct netbuf *addr,
2N/A uint_t max_msgsize,
2N/A int retries,
2N/A struct cred *cred)
2N/A{
2N/A int error = 0;
2N/A int family = AF_UNSPEC;
2N/A
2N/A switch (config->knc_semantics) {
2N/A case NC_TPI_CLTS:
2N/A clnt_clts_kinit(h, addr, retries, cred);
2N/A break;
2N/A case NC_TPI_COTS:
2N/A case NC_TPI_COTS_ORD:
2N/A RPCLOG0(2, "clnt_tli_kinit: COTS selected\n");
2N/A if (strcmp(config->knc_protofmly, NC_INET) == 0)
2N/A family = AF_INET;
2N/A else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
2N/A family = AF_INET6;
2N/A clnt_cots_kinit(h, config->knc_rdev, family,
2N/A addr, max_msgsize, cred);
2N/A break;
2N/A case NC_TPI_RDMA:
2N/A RPCLOG0(2, "clnt_tli_kinit: RDMA selected\n");
2N/A clnt_rdma_kinit(h, config->knc_proto,
2N/A (void *)config->knc_rdev, addr, cred);
2N/A break;
2N/A default:
2N/A error = EINVAL;
2N/A }
2N/A
2N/A return (error);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * try to bind to a reserved port
2N/A */
2N/Aint
2N/Abindresvport(
2N/A TIUSER *tiptr,
2N/A struct netbuf *addr,
2N/A struct netbuf *bound_addr,
2N/A bool_t tcp)
2N/A{
2N/A struct sockaddr_in *sin;
2N/A struct sockaddr_in6 *sin6;
2N/A bool_t ipv6_flag = 0;
2N/A int i;
2N/A struct t_bind *req;
2N/A struct t_bind *ret;
2N/A int error;
2N/A bool_t loop_twice;
2N/A int start;
2N/A int stop;
2N/A ushort_t *last_used;
2N/A
2N/A if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&req)) != 0) {
2N/A RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
2N/A return (error);
2N/A }
2N/A
2N/A if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&ret)) != 0) {
2N/A RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
2N/A (void) t_kfree(tiptr, (char *)req, T_BIND);
2N/A return (error);
2N/A }
2N/A
2N/A /* now separate IPv4 and IPv6 by looking at len of tiptr.addr */
2N/A if (tiptr->tp_info.addr == sizeof (struct sockaddr_in6)) {
2N/A /* it's IPv6 */
2N/A ipv6_flag = 1;
2N/A sin6 = (struct sockaddr_in6 *)req->addr.buf;
2N/A sin6->sin6_family = AF_INET6;
2N/A bzero((char *)&sin6->sin6_addr, sizeof (struct in6_addr));
2N/A req->addr.len = sizeof (struct sockaddr_in6);
2N/A } else {
2N/A /* LINTED pointer alignment */
2N/A sin = (struct sockaddr_in *)req->addr.buf;
2N/A sin->sin_family = AF_INET;
2N/A sin->sin_addr.s_addr = INADDR_ANY;
2N/A req->addr.len = sizeof (struct sockaddr_in);
2N/A }
2N/A
2N/A /*
2N/A * Caller wants to bind to a specific port, so don't bother with the
2N/A * loop that binds to the next free one.
2N/A */
2N/A if (addr) {
2N/A if (ipv6_flag) {
2N/A sin6->sin6_port =
2N/A ((struct sockaddr_in6 *)addr->buf)->sin6_port;
2N/A } else {
2N/A sin->sin_port =
2N/A ((struct sockaddr_in *)addr->buf)->sin_port;
2N/A }
2N/A RPCLOG(8, "bindresvport: calling t_kbind tiptr = %p\n",
2N/A (void *)tiptr);
2N/A if ((error = t_kbind(tiptr, req, ret)) != 0) {
2N/A RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
2N/A /*
2N/A * The unbind is called in case the bind failed
2N/A * with an EINTR potentially leaving the
2N/A * transport in bound state.
2N/A */
2N/A if (error == EINTR)
2N/A (void) t_kunbind(tiptr);
2N/A } else if (bcmp(req->addr.buf, ret->addr.buf,
2N/A ret->addr.len) != 0) {
2N/A RPCLOG0(1, "bindresvport: bcmp error\n");
2N/A (void) t_kunbind(tiptr);
2N/A error = EADDRINUSE;
2N/A }
2N/A } else {
2N/A if (tcp)
2N/A last_used = &clnt_tcp_last_used;
2N/A else
2N/A last_used = &clnt_udp_last_used;
2N/A error = EADDRINUSE;
2N/A stop = MIN_PRIV;
2N/A
2N/A start = (*last_used == MIN_PRIV ? MAX_PRIV : *last_used - 1);
2N/A loop_twice = (start < MAX_PRIV ? TRUE : FALSE);
2N/A
2N/Abindresvport_again:
2N/A for (i = start;
2N/A (error == EADDRINUSE || error == EADDRNOTAVAIL) &&
2N/A i >= stop; i--) {
2N/A if (ipv6_flag)
2N/A sin6->sin6_port = htons(i);
2N/A else
2N/A sin->sin_port = htons(i);
2N/A RPCLOG(8, "bindresvport: calling t_kbind tiptr = 0%p\n",
2N/A (void *)tiptr);
2N/A if ((error = t_kbind(tiptr, req, ret)) != 0) {
2N/A RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
2N/A /*
2N/A * The unbind is called in case the bind failed
2N/A * with an EINTR potentially leaving the
* transport in bound state.
*/
if (error == EINTR)
(void) t_kunbind(tiptr);
} else if (bcmp(req->addr.buf, ret->addr.buf,
ret->addr.len) != 0) {
RPCLOG0(1, "bindresvport: bcmp error\n");
(void) t_kunbind(tiptr);
error = EADDRINUSE;
} else
error = 0;
}
if (!error) {
if (ipv6_flag) {
RPCLOG(8, "bindresvport: port assigned %d\n",
sin6->sin6_port);
*last_used = ntohs(sin6->sin6_port);
} else {
RPCLOG(8, "bindresvport: port assigned %d\n",
sin->sin_port);
*last_used = ntohs(sin->sin_port);
}
} else if (loop_twice) {
loop_twice = FALSE;
start = MAX_PRIV;
stop = *last_used + 1;
goto bindresvport_again;
}
}
if (!error && bound_addr) {
if (bound_addr->maxlen < ret->addr.len) {
kmem_free(bound_addr->buf, bound_addr->maxlen);
bound_addr->buf = kmem_zalloc(ret->addr.len, KM_SLEEP);
bound_addr->maxlen = ret->addr.len;
}
bcopy(ret->addr.buf, bound_addr->buf, ret->addr.len);
bound_addr->len = ret->addr.len;
}
(void) t_kfree(tiptr, (char *)req, T_BIND);
(void) t_kfree(tiptr, (char *)ret, T_BIND);
return (error);
}
void
clnt_init(void)
{
clnt_cots_init();
clnt_clts_init();
}
void
clnt_fini(void)
{
clnt_clts_fini();
clnt_cots_fini();
}
call_table_t *
call_table_init(int size)
{
call_table_t *ctp;
int i;
ctp = kmem_alloc(sizeof (call_table_t) * size, KM_SLEEP);
for (i = 0; i < size; i++) {
ctp[i].ct_call_next = (calllist_t *)&ctp[i];
ctp[i].ct_call_prev = (calllist_t *)&ctp[i];
mutex_init(&ctp[i].ct_lock, NULL, MUTEX_DEFAULT, NULL);
ctp[i].ct_len = 0;
}
return (ctp);
}