#include <sys/param.h>
#include <sys/types.h>
#include <rpc/types.h>
#include <netinet/in.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <sys/tiuser.h>
#include <sys/t_kuser.h>
#include <rpc/svc.h>
#include <rpc/xdr.h>
#include <sys/file.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/stream.h>
#include <sys/tihdr.h>
#include <sys/fcntl.h>
#include <sys/socket.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#define NC_INET "inet"
#define MAX_PRIV (IPPORT_RESERVED-1)
#define MIN_PRIV (IPPORT_RESERVED/2)
ushort_t clnt_udp_last_used = MIN_PRIV;
ushort_t clnt_tcp_last_used = MIN_PRIV;
int
clnt_tli_kcreate(
struct knetconfig *config,
struct netbuf *svcaddr,
rpcprog_t prog,
rpcvers_t vers,
uint_t max_msgsize,
int retries,
struct cred *cred,
CLIENT **ncl)
{
CLIENT *cl;
int error;
int family = AF_UNSPEC;
error = 0;
cl = NULL;
RPCLOG(8, "clnt_tli_kcreate: prog %x", prog);
RPCLOG(8, ", vers %d", vers);
RPCLOG(8, ", knc_semantics %d", config->knc_semantics);
RPCLOG(8, ", knc_protofmly %s", config->knc_protofmly);
RPCLOG(8, ", knc_proto %s\n", config->knc_proto);
if (config == NULL || config->knc_protofmly == NULL || ncl == NULL) {
RPCLOG0(1, "clnt_tli_kcreate: bad config or handle\n");
return (EINVAL);
}
switch (config->knc_semantics) {
case NC_TPI_CLTS:
RPCLOG0(8, "clnt_tli_kcreate: CLTS selected\n");
error = clnt_clts_kcreate(config, svcaddr, prog, vers,
retries, cred, &cl);
if (error != 0) {
RPCLOG(1,
"clnt_tli_kcreate: clnt_clts_kcreate failed error %d\n",
error);
return (error);
}
break;
case NC_TPI_COTS:
case NC_TPI_COTS_ORD:
RPCLOG0(8, "clnt_tli_kcreate: COTS selected\n");
if (strcmp(config->knc_protofmly, NC_INET) == 0)
family = AF_INET;
else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
family = AF_INET6;
error = clnt_cots_kcreate(config->knc_rdev, svcaddr, family,
prog, vers, max_msgsize, cred, &cl);
if (error != 0) {
RPCLOG(1,
"clnt_tli_kcreate: clnt_cots_kcreate failed error %d\n",
error);
return (error);
}
break;
case NC_TPI_RDMA:
RPCLOG0(8, "clnt_tli_kcreate: RDMA selected\n");
if (is_system_labeled()) {
RPCLOG0(1, "clnt_tli_kcreate: tsol not supported\n");
return (EPROTONOSUPPORT);
}
if (strcmp(config->knc_protofmly, NC_INET) == 0)
family = AF_INET;
else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
family = AF_INET6;
error = clnt_rdma_kcreate(config->knc_proto,
(void *)config->knc_rdev, svcaddr, family, prog, vers, cred,
&cl);
if (error != 0) {
RPCLOG(1,
"clnt_tli_kcreate: clnt_rdma_kcreate failed error %d\n",
error);
return (error);
}
break;
default:
error = EINVAL;
RPCLOG(1, "clnt_tli_kcreate: Bad service type %d\n",
config->knc_semantics);
return (error);
}
*ncl = cl;
return (0);
}
int
clnt_tli_kinit(
CLIENT *h,
struct knetconfig *config,
struct netbuf *addr,
uint_t max_msgsize,
int retries,
struct cred *cred)
{
int error = 0;
int family = AF_UNSPEC;
switch (config->knc_semantics) {
case NC_TPI_CLTS:
clnt_clts_kinit(h, addr, retries, cred);
break;
case NC_TPI_COTS:
case NC_TPI_COTS_ORD:
RPCLOG0(2, "clnt_tli_kinit: COTS selected\n");
if (strcmp(config->knc_protofmly, NC_INET) == 0)
family = AF_INET;
else if (strcmp(config->knc_protofmly, NC_INET6) == 0)
family = AF_INET6;
clnt_cots_kinit(h, config->knc_rdev, family,
addr, max_msgsize, cred);
break;
case NC_TPI_RDMA:
RPCLOG0(2, "clnt_tli_kinit: RDMA selected\n");
clnt_rdma_kinit(h, config->knc_proto,
(void *)config->knc_rdev, addr, cred);
break;
default:
error = EINVAL;
}
return (error);
}
int
bindresvport(
TIUSER *tiptr,
struct netbuf *addr,
struct netbuf *bound_addr,
bool_t tcp)
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
bool_t ipv6_flag = 0;
int i;
struct t_bind *req;
struct t_bind *ret;
int error;
bool_t loop_twice;
int start;
int stop;
ushort_t *last_used;
if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&req)) != 0) {
RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
return (error);
}
if ((error = t_kalloc(tiptr, T_BIND, T_ADDR, (char **)&ret)) != 0) {
RPCLOG(1, "bindresvport: t_kalloc %d\n", error);
(void) t_kfree(tiptr, (char *)req, T_BIND);
return (error);
}
if (tiptr->tp_info.addr == sizeof (struct sockaddr_in6)) {
ipv6_flag = 1;
sin6 = (struct sockaddr_in6 *)req->addr.buf;
sin6->sin6_family = AF_INET6;
bzero((char *)&sin6->sin6_addr, sizeof (struct in6_addr));
req->addr.len = sizeof (struct sockaddr_in6);
} else {
sin = (struct sockaddr_in *)req->addr.buf;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = INADDR_ANY;
req->addr.len = sizeof (struct sockaddr_in);
}
int useresvport = 0;
if (addr) {
if (ipv6_flag) {
bcopy(addr->buf, (char *)sin6,
sizeof (struct sockaddr_in6));
if (sin6->sin6_port != 0) {
useresvport = 1;
}
} else {
bcopy(addr->buf, req->addr.buf, addr->len);
if (sin->sin_port != 0) {
useresvport = 1;
}
}
req->addr.len = addr->len;
RPCLOG(8, "bindresvport: calling t_kbind useresvport = %d\n",
useresvport);
}
if (useresvport) {
if ((error = t_kbind(tiptr, req, ret)) != 0) {
RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
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 {
if (tcp)
last_used = &clnt_tcp_last_used;
else
last_used = &clnt_udp_last_used;
error = EADDRINUSE;
stop = MIN_PRIV;
start = (*last_used == MIN_PRIV ? MAX_PRIV : *last_used - 1);
loop_twice = (start < MAX_PRIV ? TRUE : FALSE);
bindresvport_again:
for (i = start;
(error == EADDRINUSE || error == EADDRNOTAVAIL) &&
i >= stop; i--) {
if (ipv6_flag)
sin6->sin6_port = htons(i);
else
sin->sin_port = htons(i);
RPCLOG(8, "bindresvport: calling t_kbind tiptr = 0%p\n",
(void *)tiptr);
if ((error = t_kbind(tiptr, req, ret)) != 0) {
RPCLOG(1, "bindresvport: t_kbind: %d\n", error);
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);
}
void
clnt_init_netbuf(struct netbuf *nbuf, int len)
{
nbuf->buf = kmem_zalloc(len, KM_SLEEP);
nbuf->maxlen = len;
nbuf->len = 0;
}
void
clnt_free_netbuf(struct netbuf *nbuf)
{
if (nbuf == NULL || nbuf->buf == NULL) {
#ifdef DEBUG
cmn_err(CE_PANIC, "Null netbuf in clnt_free_netbuf");
#endif
return;
}
kmem_free(nbuf->buf, nbuf->maxlen);
nbuf->buf = NULL;
nbuf->maxlen = 0;
nbuf->len = 0;
}
void
clnt_dup_netbuf(const struct netbuf *from, struct netbuf *to)
{
clnt_init_netbuf(to, from->len);
to->len = from->len;
bcopy(from->buf, to->buf, (size_t)from->len);
}
int
clnt_cmp_netaddr(const struct netbuf *from, struct netbuf *to)
{
if (to->len != from->len || from->len == 0)
return (1);
struct sockaddr *saddr = (struct sockaddr *)(from->buf);
struct sockaddr *toaddr = (struct sockaddr *)(to->buf);
if (saddr->sa_family == AF_INET && toaddr->sa_family == AF_INET) {
return bcmp(&((struct sockaddr_in *)from->buf)->sin_addr,
&((struct sockaddr_in *)to->buf)->sin_addr,
sizeof (struct in_addr));
} else if (saddr->sa_family == AF_INET6 &&
toaddr->sa_family == AF_INET6) {
return bcmp(&(((struct sockaddr_in6 *)from->buf)->sin6_addr),
&(((struct sockaddr_in6 *)to->buf)->sin6_addr),
sizeof (struct in6_addr));
}
return (1);
}