Print this page
8330 Add svc_tp_create_addr to libnsl
Reviewed by: Paul Dagnelie <pcd@delphix.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sebastien Roy <sebastien.roy@delphix.com>
@@ -18,12 +18,12 @@
*
* CDDL HEADER END
*/
/*
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
@@ -85,10 +85,14 @@
extern mutex_t xprtlist_lock;
static SVCXPRT * svc_tli_create_common(int, const struct netconfig *,
const struct t_bind *, uint_t, uint_t, boolean_t);
+static SVCXPRT *svc_tp_create_bind(void (*dispatch)(),
+ const rpcprog_t, const rpcvers_t,
+ const struct netconfig *, const struct t_bind *);
+
boolean_t
is_multilevel(rpcprog_t prognum)
{
/* This is a list of identified multilevel service provider */
if ((prognum == MOUNTPROG) || (prognum == NFS_PROGRAM) ||
@@ -174,16 +178,55 @@
}
/*
* The high level interface to svc_tli_create().
* It tries to create a server for "nconf" and registers the service
- * with the rpcbind. It calls svc_tli_create();
+ * with the rpcbind.
*/
SVCXPRT *
svc_tp_create(void (*dispatch)(), const rpcprog_t prognum,
const rpcvers_t versnum, const struct netconfig *nconf)
{
+ return (svc_tp_create_bind(dispatch, prognum, versnum, nconf, NULL));
+}
+
+/*
+ * svc_tp_create_addr()
+ * Variant of svc_tp_create() that allows specifying just the
+ * the binding address, for convenience.
+ */
+SVCXPRT *
+svc_tp_create_addr(void (*dispatch)(), const rpcprog_t prognum,
+ const rpcvers_t versnum, const struct netconfig *nconf,
+ const struct netbuf *addr)
+{
+ struct t_bind bind;
+ struct t_bind *bindp = NULL;
+
+ if (addr != NULL) {
+
+ bind.addr = *addr;
+ if (!rpc_control(__RPC_SVC_LSTNBKLOG_GET, &bind.qlen)) {
+ syslog(LOG_ERR,
+ "svc_tp_create: can't get listen backlog");
+ return (NULL);
+ }
+ bindp = &bind;
+ }
+
+ /*
+ * When bindp == NULL, this is the same as svc_tp_create().
+ */
+ return (svc_tp_create_bind(dispatch, prognum, versnum,
+ nconf, bindp));
+}
+
+static SVCXPRT *
+svc_tp_create_bind(void (*dispatch)(), const rpcprog_t prognum,
+ const rpcvers_t versnum, const struct netconfig *nconf,
+ const struct t_bind *bindaddr)
+{
SVCXPRT *xprt;
boolean_t anon_mlp = B_FALSE;
if (nconf == NULL) {
(void) syslog(LOG_ERR, "svc_tp_create: invalid netconfig "
@@ -192,11 +235,12 @@
}
/* Some programs need to allocate MLP for multilevel services */
if (is_system_labeled() && is_multilevel(prognum))
anon_mlp = B_TRUE;
- xprt = svc_tli_create_common(RPC_ANYFD, nconf, NULL, 0, 0, anon_mlp);
+ xprt = svc_tli_create_common(RPC_ANYFD, nconf, bindaddr, 0, 0,
+ anon_mlp);
if (xprt == NULL)
return (NULL);
(void) rpcb_unset(prognum, versnum, (struct netconfig *)nconf);
if (svc_reg(xprt, prognum, versnum, dispatch, nconf) == FALSE) {
@@ -322,11 +366,10 @@
(void) syslog(LOG_ERR, "svc_tli_create: No memory!");
goto freedata;
}
switch (state) {
- bool_t tcp, exclbind;
case T_UNBND:
/* If this is a labeled system, then ask for an MLP */
if (is_system_labeled() &&
(strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
@@ -335,36 +378,19 @@
if (mlp_flag)
(void) __rpc_tli_set_options(fd, SOL_SOCKET,
SO_ANON_MLP, 1);
}
+ if (bindaddr) {
/*
- * SO_EXCLBIND has the following properties
- * - an fd bound to port P via IPv4 will prevent an IPv6
- * bind to port P (and vice versa)
- * - an fd bound to a wildcard IP address for port P will
- * prevent a more specific IP address bind to port P
- * (see {tcp,udp}.c for details)
- *
- * We use the latter property to prevent hijacking of RPC
- * services that reside at non-privileged ports.
+ * Services that specify a bind address typically
+ * use a fixed service (IP port) so we need to set
+ * SO_REUSEADDR to prevent bind errors on restart.
*/
- tcp = nconf ? (strcmp(nconf->nc_proto, NC_TCP) == 0) : 0;
- if (nconf &&
- (tcp || (strcmp(nconf->nc_proto, NC_UDP) == 0)) &&
- rpc_control(__RPC_SVC_EXCLBIND_GET, &exclbind)) {
- if (exclbind) {
- if (__rpc_tli_set_options(fd, SOL_SOCKET,
- SO_EXCLBIND, 1) < 0) {
- syslog(LOG_ERR,
- "svc_tli_create: can't set EXCLBIND [netid='%s']",
- nconf->nc_netid);
- goto freedata;
- }
- }
- }
- if (bindaddr) {
+ if (bindaddr->addr.len != 0)
+ (void) __rpc_tli_set_options(fd, SOL_SOCKET,
+ SO_REUSEADDR, 1);
if (t_bind(fd, (struct t_bind *)bindaddr, tres) == -1) {
char errorstr[100];
__tli_sys_strerror(errorstr, sizeof (errorstr),
t_errno, errno);
@@ -401,10 +427,51 @@
(void) syslog(LOG_ERR,
"svc_tli_create: could not bind: %s",
errorstr);
goto freedata;
}
+ }
+
+ /*
+ * If requested, set SO_EXCLBIND on each binding.
+ *
+ * SO_EXCLBIND has the following properties
+ * - an fd bound to port P via IPv4 will prevent an IPv6
+ * bind to port P (and vice versa)
+ * - an fd bound to a wildcard IP address for port P will
+ * prevent a more specific IP address bind to port P
+ * (see {tcp,udp}.c for details)
+ *
+ * We use the latter property to prevent hijacking of RPC
+ * services that reside at non-privileged ports.
+ *
+ * When the bind address is not specified, each bind gets a
+ * new port number, and (for IP transports) we should set
+ * the exclusive flag after every IP bind. That's the
+ * strcmp nc_proto part of the expression below.
+ *
+ * When the bind address IS specified, we need to set the
+ * exclusive flag only after we've bound both IPv6+IPv4,
+ * or the IPv4 bind will fail. Setting the exclusive flag
+ * after the "tcp" or "udp" transport bind does that.
+ * That's the strcmp nc_netid part below.
+ */
+ if (nconf != NULL && ((bindaddr == NULL &&
+ (strcmp(nconf->nc_proto, NC_TCP) == 0 ||
+ strcmp(nconf->nc_proto, NC_UDP) == 0)) ||
+ (strcmp(nconf->nc_netid, "tcp") == 0 ||
+ strcmp(nconf->nc_netid, "udp") == 0))) {
+ bool_t exclbind = FALSE;
+ (void) rpc_control(__RPC_SVC_EXCLBIND_GET, &exclbind);
+ if (exclbind &&
+ __rpc_tli_set_options(fd, SOL_SOCKET,
+ SO_EXCLBIND, 1) < 0) {
+ syslog(LOG_ERR,
+ "svc_tli_create: can't set EXCLBIND [netid='%s']",
+ nconf->nc_netid);
+ goto freedata;
+ }
}
/* Enable options of returning the ip's for udp */
if (nconf) {
int ret = 0;