Mercurial > illumos > illumos-gate
changeset 13569:954bbadc2ca4
1982 Make rpcbind_getaddr() work with all versions of rpcbind protocol
Reviewed by: Gordon Ross <gwr@nexenta.com>
Reviewed by: Dan McDonald <danmcd@nexenta.com>
Approved by: Eric Schrock <Eric.Schrock@delphix.com>
author | Dan Kruchinin <dan.kruchinin@nexenta.com> |
---|---|
date | Mon, 23 Jan 2012 07:43:36 -0800 |
parents | a53b998f5e1b |
children | 3411fd5f1589 |
files | usr/src/uts/common/rpc/rpc_subr.c |
diffstat | 1 files changed, 155 insertions(+), 39 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/rpc/rpc_subr.c Thu Jan 19 15:14:24 2012 +0200 +++ b/usr/src/uts/common/rpc/rpc_subr.c Mon Jan 23 07:43:36 2012 -0800 @@ -27,8 +27,10 @@ * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. + */ -#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.1 */ /* @@ -49,6 +51,7 @@ #include <sys/systm.h> #include <sys/cmn_err.h> #include <sys/debug.h> +#include <sys/sdt.h> #include <netinet/in.h> #include <rpc/types.h> #include <rpc/auth.h> @@ -408,11 +411,97 @@ } /* + * XXX: xdr_pmap is here, because it's the only XDR function + * of portmap protocol. If there'll be more portmap functions, + * it would be better to put them to a separate file. + */ +bool_t +xdr_pmap(XDR *xdrs, PMAP *objp) +{ + if (!xdr_rpcprog(xdrs, &objp->pm_prog)) + return (FALSE); + if (!xdr_rpcvers(xdrs, &objp->pm_vers)) + return (FALSE); + if (!xdr_rpcprot(xdrs, &objp->pm_prot)) + return (FALSE); + if (!xdr_u_int(xdrs, &objp->pm_port)) + return (FALSE); + + return (TRUE); +} + +/* + * Get remote port via PORTMAP protocol version 2 (works for IPv4 only) + * according to RFC 1833, section 3. + */ +static enum clnt_stat +portmap_getport(struct knetconfig *config, rpcprog_t prog, rpcvers_t vers, + struct netbuf *addr, struct timeval tmo) +{ + enum clnt_stat status; + CLIENT *client = NULL; + k_sigset_t oldmask; + k_sigset_t newmask; + ushort_t port = 0; + struct pmap parms; + + ASSERT(strcmp(config->knc_protofmly, NC_INET) == 0); + + bzero(&parms, sizeof (parms)); + parms.pm_prog = prog; + parms.pm_vers = vers; + if (strcmp(config->knc_proto, NC_TCP) == 0) { + parms.pm_prot = IPPROTO_TCP; + } else { /* NC_UDP */ + parms.pm_prot = IPPROTO_UDP; + } + + + /* + * Mask all signals before doing RPC network operations + * in the same way rpcbind_getaddr() does (see comments + * there). + */ + sigfillset(&newmask); + sigreplace(&newmask, &oldmask); + + if (clnt_tli_kcreate(config, addr, PMAPPROG, + PMAPVERS, 0, 0, CRED(), &client)) { + sigreplace(&oldmask, (k_sigset_t *)NULL); + return (RPC_TLIERROR); + } + + client->cl_nosignal = 1; + status = CLNT_CALL(client, PMAPPROC_GETPORT, + xdr_pmap, (char *)&parms, + xdr_u_short, (char *)&port, tmo); + + sigreplace(&oldmask, (k_sigset_t *)NULL); + if (status != RPC_SUCCESS) + goto out; + if (port == 0) { + status = RPC_PROGNOTREGISTERED; + goto out; + } + + put_inet_port(addr, ntohs(port)); + +out: + auth_destroy(client->cl_auth); + clnt_destroy(client); + + return (status); +} + +/* * Try to get the address for the desired service by using the rpcbind * protocol. Ignores signals. If addr is a loopback address, it is * expected to be initialized to "<hostname>.". + * rpcbind_getaddr() is able to work with RPCBIND protocol version 3 and 4 + * and PORTMAP protocol version 2. + * It tries version 4 at first, then version 3 and finally (if both failed) + * it tries portmapper protocol version 2. */ - enum clnt_stat rpcbind_getaddr(struct knetconfig *config, rpcprog_t prog, rpcvers_t vers, struct netbuf *addr) @@ -421,11 +510,11 @@ enum clnt_stat status; RPCB parms; struct timeval tmo; - CLIENT *client = NULL; k_sigset_t oldmask; k_sigset_t newmask; ushort_t port; int iptype; + rpcvers_t rpcbv; /* * Call rpcbind (local or remote) to get an address we can use @@ -438,11 +527,13 @@ parms.r_addr = parms.r_owner = ""; if (strcmp(config->knc_protofmly, NC_INET) == 0) { + put_inet_port(addr, htons(PMAPPORT)); + if (strcmp(config->knc_proto, NC_TCP) == 0) parms.r_netid = "tcp"; else parms.r_netid = "udp"; - put_inet_port(addr, htons(PMAPPORT)); + } else if (strcmp(config->knc_protofmly, NC_INET6) == 0) { if (strcmp(config->knc_proto, NC_TCP) == 0) parms.r_netid = "tcp6"; @@ -465,39 +556,55 @@ } /* - * Mask signals for the duration of the handle creation and - * RPC calls. This allows relatively normal operation with a - * signal already posted to our thread (e.g., when we are - * sending an NLM_CANCEL in response to catching a signal). - * - * Any further exit paths from this routine must restore - * the original signal mask. + * Try RPCBIND versions 4 and 3 (if 4 fails). */ - sigfillset(&newmask); - sigreplace(&newmask, &oldmask); + for (rpcbv = RPCBVERS4; rpcbv >= RPCBVERS; rpcbv--) { + CLIENT *client = NULL; + + if (ua != NULL) { + xdr_free(xdr_wrapstring, (char *)&ua); + ua = NULL; + } + + /* + * Mask signals for the duration of the handle creation and + * RPC calls. This allows relatively normal operation with a + * signal already posted to our thread (e.g., when we are + * sending an NLM_CANCEL in response to catching a signal). + * + * Any further exit paths from this routine must restore + * the original signal mask. + */ + sigfillset(&newmask); + sigreplace(&newmask, &oldmask); - if (clnt_tli_kcreate(config, addr, RPCBPROG, - RPCBVERS, 0, 0, CRED(), &client)) { - status = RPC_TLIERROR; + if (clnt_tli_kcreate(config, addr, RPCBPROG, + rpcbv, 0, 0, CRED(), &client)) { + status = RPC_TLIERROR; + sigreplace(&oldmask, (k_sigset_t *)NULL); + continue; + } + + client->cl_nosignal = 1; + status = CLNT_CALL(client, RPCBPROC_GETADDR, + xdr_rpcb, (char *)&parms, + xdr_wrapstring, (char *)&ua, tmo); + sigreplace(&oldmask, (k_sigset_t *)NULL); - goto out; + auth_destroy(client->cl_auth); + clnt_destroy(client); + + if (status == RPC_SUCCESS) { + if (ua == NULL || *ua == NULL) { + status = RPC_PROGNOTREGISTERED; + continue; + } + + break; + } } - - client->cl_nosignal = 1; - if ((status = CLNT_CALL(client, RPCBPROC_GETADDR, - xdr_rpcb, (char *)&parms, - xdr_wrapstring, (char *)&ua, - tmo)) != RPC_SUCCESS) { - sigreplace(&oldmask, (k_sigset_t *)NULL); - goto out; - } - - sigreplace(&oldmask, (k_sigset_t *)NULL); - - if (ua == NULL || *ua == NULL) { - status = RPC_PROGNOTREGISTERED; - goto out; - } + if (status != RPC_SUCCESS) + goto try_portmap; /* * Convert the universal address to the transport address. @@ -513,7 +620,7 @@ /* make sure that the ip address is the correct type */ if (rpc_iptype(ua, &iptype) != 0) { status = RPC_UNKNOWNADDR; - goto out; + goto try_portmap; } port = rpc_uaddr2port(iptype, ua); put_inet_port(addr, ntohs(port)); @@ -521,7 +628,7 @@ /* make sure that the ip address is the correct type */ if (rpc_iptype(ua, &iptype) != 0) { status = RPC_UNKNOWNADDR; - goto out; + goto try_portmap; } port = rpc_uaddr2port(iptype, ua); put_inet6_port(addr, ntohs(port)); @@ -532,11 +639,20 @@ cmn_err(CE_PANIC, "rpcbind_getaddr: bad protocol family"); } +try_portmap: + if (status != RPC_SUCCESS && + strcmp(config->knc_protofmly, NC_INET) == 0) { + /* + * For IPv4 try to get remote port via PORTMAP protocol. + * NOTE: if we're here, then all attempts to get remote + * port via RPCBIND protocol failed. + */ + + DTRACE_PROBE1(try__portmap, enum clnt_stat, status); + status = portmap_getport(config, prog, vers, addr, tmo); + } + out: - if (client != NULL) { - auth_destroy(client->cl_auth); - clnt_destroy(client); - } if (ua != NULL) xdr_free(xdr_wrapstring, (char *)&ua); return (status);