cregit-Linux how code gets into the kernel

Release 4.11 net/sunrpc/rpcb_clnt.c

Directory: net/sunrpc
/*
 * In-kernel rpcbind client supporting versions 2, 3, and 4 of the rpcbind
 * protocol
 *
 * Based on RFC 1833: "Binding Protocols for ONC RPC Version 2" and
 * RFC 3530: "Network File System (NFS) version 4 Protocol"
 *
 * Original: Gilles Quillard, Bull Open Source, 2005 <gilles.quillard@bull.net>
 * Updated: Chuck Lever, Oracle Corporation, 2007 <chuck.lever@oracle.com>
 *
 * Descended from net/sunrpc/pmap_clnt.c,
 *  Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
 */

#include <linux/module.h>

#include <linux/types.h>
#include <linux/socket.h>
#include <linux/un.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <net/ipv6.h>

#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/addr.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/xprtsock.h>

#include "netns.h"

#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)

# define RPCDBG_FACILITY	RPCDBG_BIND
#endif


#define RPCBIND_SOCK_PATHNAME	"/var/run/rpcbind.sock"


#define RPCBIND_PROGRAM		(100000u)

#define RPCBIND_PORT		(111u)


#define RPCBVERS_2		(2u)

#define RPCBVERS_3		(3u)

#define RPCBVERS_4		(4u)

enum {
	
RPCBPROC_NULL,
	
RPCBPROC_SET,
	
RPCBPROC_UNSET,
	
RPCBPROC_GETPORT,
	
RPCBPROC_GETADDR = 3,		/* alias for GETPORT */
	
RPCBPROC_DUMP,
	
RPCBPROC_CALLIT,
	
RPCBPROC_BCAST = 5,		/* alias for CALLIT */
	
RPCBPROC_GETTIME,
	
RPCBPROC_UADDR2TADDR,
	
RPCBPROC_TADDR2UADDR,
	
RPCBPROC_GETVERSADDR,
	
RPCBPROC_INDIRECT,
	
RPCBPROC_GETADDRLIST,
	
RPCBPROC_GETSTAT,
};

/*
 * r_owner
 *
 * The "owner" is allowed to unset a service in the rpcbind database.
 *
 * For AF_LOCAL SET/UNSET requests, rpcbind treats this string as a
 * UID which it maps to a local user name via a password lookup.
 * In all other cases it is ignored.
 *
 * For SET/UNSET requests, user space provides a value, even for
 * network requests, and GETADDR uses an empty string.  We follow
 * those precedents here.
 */

#define RPCB_OWNER_STRING	"0"

#define RPCB_MAXOWNERLEN	sizeof(RPCB_OWNER_STRING)

/*
 * XDR data type sizes
 */

#define RPCB_program_sz		(1)

#define RPCB_version_sz		(1)

#define RPCB_protocol_sz	(1)

#define RPCB_port_sz		(1)

#define RPCB_boolean_sz		(1)


#define RPCB_netid_sz		(1 + XDR_QUADLEN(RPCBIND_MAXNETIDLEN))

#define RPCB_addr_sz		(1 + XDR_QUADLEN(RPCBIND_MAXUADDRLEN))

#define RPCB_ownerstring_sz	(1 + XDR_QUADLEN(RPCB_MAXOWNERLEN))

/*
 * XDR argument and result sizes
 */

#define RPCB_mappingargs_sz	(RPCB_program_sz + RPCB_version_sz + \
                                RPCB_protocol_sz + RPCB_port_sz)

#define RPCB_getaddrargs_sz	(RPCB_program_sz + RPCB_version_sz + \
                                RPCB_netid_sz + RPCB_addr_sz + \
                                RPCB_ownerstring_sz)


#define RPCB_getportres_sz	RPCB_port_sz

#define RPCB_setres_sz		RPCB_boolean_sz

/*
 * Note that RFC 1833 does not put any size restrictions on the
 * address string returned by the remote rpcbind database.
 */

#define RPCB_getaddrres_sz	RPCB_addr_sz

static void			rpcb_getport_done(struct rpc_task *, void *);
static void			rpcb_map_release(void *data);

static const struct rpc_program	rpcb_program;


struct rpcbind_args {
	
struct rpc_xprt *	r_xprt;

	
u32			r_prog;
	
u32			r_vers;
	
u32			r_prot;
	
unsigned short		r_port;
	
const char *		r_netid;
	
const char *		r_addr;
	
const char *		r_owner;

	
int			r_status;
};


static struct rpc_procinfo rpcb_procedures2[];

static struct rpc_procinfo rpcb_procedures3[];

static struct rpc_procinfo rpcb_procedures4[];


struct rpcb_info {
	
u32			rpc_vers;
	
struct rpc_procinfo *	rpc_proc;
};


static const struct rpcb_info rpcb_next_version[];

static const struct rpcb_info rpcb_next_version6[];


static const struct rpc_call_ops rpcb_getport_ops = {
	.rpc_call_done		= rpcb_getport_done,
	.rpc_release		= rpcb_map_release,
};


static void rpcb_wake_rpcbind_waiters(struct rpc_xprt *xprt, int status) { xprt_clear_binding(xprt); rpc_wake_up_status(&xprt->binding, status); }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever29100.00%1100.00%
Total29100.00%1100.00%


static void rpcb_map_release(void *data) { struct rpcbind_args *map = data; rpcb_wake_rpcbind_waiters(map->r_xprt, map->r_status); xprt_put(map->r_xprt); kfree(map->r_addr); kfree(map); }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust4085.11%150.00%
Chuck Lever714.89%150.00%
Total47100.00%2100.00%


static int rpcb_get_local(struct net *net) { int cnt; struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); spin_lock(&sn->rpcb_clnt_lock); if (sn->rpcb_users) sn->rpcb_users++; cnt = sn->rpcb_users; spin_unlock(&sn->rpcb_clnt_lock); return cnt; }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky62100.00%3100.00%
Total62100.00%3100.00%


void rpcb_put_local(struct net *net) { struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); struct rpc_clnt *clnt = sn->rpcb_local_clnt; struct rpc_clnt *clnt4 = sn->rpcb_local_clnt4; int shutdown = 0; spin_lock(&sn->rpcb_clnt_lock); if (sn->rpcb_users) { if (--sn->rpcb_users == 0) { sn->rpcb_local_clnt = NULL; sn->rpcb_local_clnt4 = NULL; } shutdown = !sn->rpcb_users; } spin_unlock(&sn->rpcb_clnt_lock); if (shutdown) { /* * cleanup_rpcb_clnt - remove xprtsock's sysctls, unregister */ if (clnt4) rpc_shutdown_client(clnt4); if (clnt) rpc_shutdown_client(clnt); } }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky124100.00%4100.00%
Total124100.00%4100.00%


static void rpcb_set_local(struct net *net, struct rpc_clnt *clnt, struct rpc_clnt *clnt4, bool is_af_local) { struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); /* Protected by rpcb_create_local_mutex */ sn->rpcb_local_clnt = clnt; sn->rpcb_local_clnt4 = clnt4; sn->rpcb_is_af_local = is_af_local ? 1 : 0; smp_wmb(); sn->rpcb_users = 1; dprintk("RPC: created new rpcb local clients (rpcb_local_clnt: " "%p, rpcb_local_clnt4: %p) for net %p%s\n", sn->rpcb_local_clnt, sn->rpcb_local_clnt4, net, (net == &init_net) ? " (init_net)" : ""); }

Contributors

PersonTokensPropCommitsCommitProp
Stanislav Kinsbursky8286.32%375.00%
Trond Myklebust1313.68%125.00%
Total95100.00%4100.00%

/* * Returns zero on success, otherwise a negative errno value * is returned. */
static int rpcb_create_local_unix(struct net *net) { static const struct sockaddr_un rpcb_localaddr_rpcbind = { .sun_family = AF_LOCAL, .sun_path = RPCBIND_SOCK_PATHNAME, }; struct rpc_create_args args = { .net = net, .protocol = XPRT_TRANSPORT_LOCAL, .address = (struct sockaddr *)&rpcb_localaddr_rpcbind, .addrsize = sizeof(rpcb_localaddr_rpcbind), .servername = "localhost", .program = &rpcb_program, .version = RPCBVERS_2, .authflavor = RPC_AUTH_NULL, /* * We turn off the idle timeout to prevent the kernel * from automatically disconnecting the socket. * Otherwise, we'd have to cache the mount namespace * of the caller and somehow pass that to the socket * reconnect code. */ .flags = RPC_CLNT_CREATE_NO_IDLE_TIMEOUT, }; struct rpc_clnt *clnt, *clnt4; int result = 0; /* * Because we requested an RPC PING at transport creation time, * this works only if the user space portmapper is rpcbind, and * it's listening on AF_LOCAL on the named socket. */ clnt = rpc_create(&args); if (IS_ERR(clnt)) { dprintk("RPC: failed to create AF_LOCAL rpcbind " "client (errno %ld).\n", PTR_ERR(clnt)); result = PTR_ERR(clnt); goto out; } clnt4 = rpc_bind_new_program(clnt, &rpcb_program, RPCBVERS_4); if (IS_ERR(clnt4)) { dprintk("RPC: failed to bind second program to " "rpcbind v4 client (errno %ld).\n", PTR_ERR(clnt4)); clnt4 = NULL; } rpcb_set_local(net, clnt, clnt4, true); out: return result; }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever17690.26%342.86%
Stanislav Kinsbursky115.64%228.57%
Trond Myklebust84.10%228.57%
Total195100.00%7100.00%

/* * Returns zero on success, otherwise a negative errno value * is returned. */
static int rpcb_create_local_net(struct net *net) { static const struct sockaddr_in rpcb_inaddr_loopback = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), .sin_port = htons(RPCBIND_PORT), }; struct rpc_create_args args = { .net = net, .protocol = XPRT_TRANSPORT_TCP, .address = (struct sockaddr *)&rpcb_inaddr_loopback, .addrsize = sizeof(rpcb_inaddr_loopback), .servername = "localhost", .program = &rpcb_program, .version = RPCBVERS_2, .authflavor = RPC_AUTH_UNIX, .flags = RPC_CLNT_CREATE_NOPING, }; struct rpc_clnt *clnt, *clnt4; int result = 0; clnt = rpc_create(&args); if (IS_ERR(clnt)) { dprintk("RPC: failed to create local rpcbind " "client (errno %ld).\n", PTR_ERR(clnt)); result = PTR_ERR(clnt); goto out; } /* * This results in an RPC ping. On systems running portmapper, * the v4 ping will fail. Proceed anyway, but disallow rpcb * v4 upcalls. */ clnt4 = rpc_bind_new_program(clnt, &rpcb_program, RPCBVERS_4); if (IS_ERR(clnt4)) { dprintk("RPC: failed to bind second program to " "rpcbind v4 client (errno %ld).\n", PTR_ERR(clnt4)); clnt4 = NULL; } rpcb_set_local(net, clnt, clnt4, false); out: return result; }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever19091.79%660.00%
Stanislav Kinsbursky115.31%220.00%
Pavel Emelyanov41.93%110.00%
Trond Myklebust20.97%110.00%
Total207100.00%10100.00%

/* * Returns zero on success, otherwise a negative errno value * is returned. */
int rpcb_create_local(struct net *net) { static DEFINE_MUTEX(rpcb_create_local_mutex); int result = 0; if (rpcb_get_local(net)) return result; mutex_lock(&rpcb_create_local_mutex); if (rpcb_get_local(net)) goto out; if (rpcb_create_local_unix(net) != 0) result = rpcb_create_local_net(net); out: mutex_unlock(&rpcb_create_local_mutex); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever5675.68%350.00%
Stanislav Kinsbursky1824.32%350.00%
Total74100.00%6100.00%


static struct rpc_clnt *rpcb_create(struct net *net, const char *nodename, const char *hostname, struct sockaddr *srvaddr, size_t salen, int proto, u32 version) { struct rpc_create_args args = { .net = net, .protocol = proto, .address = srvaddr, .addrsize = salen, .servername = hostname, .nodename = nodename, .program = &rpcb_program, .version = version, .authflavor = RPC_AUTH_UNIX, .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_NONPRIVPORT), }; switch (srvaddr->sa_family) { case AF_INET: ((struct sockaddr_in *)srvaddr)->sin_port = htons(RPCBIND_PORT); break; case AF_INET6: ((struct sockaddr_in6 *)srvaddr)->sin6_port = htons(RPCBIND_PORT); break; default: return ERR_PTR(-EAFNOSUPPORT); } return rpc_create(&args); }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever13583.85%550.00%
Trond Myklebust116.83%220.00%
Pavel Emelyanov95.59%220.00%
Stanislav Kinsbursky63.73%110.00%
Total161100.00%10100.00%


static int rpcb_register_call(struct sunrpc_net *sn, struct rpc_clnt *clnt, struct rpc_message *msg, bool is_set) { int flags = RPC_TASK_NOCONNECT; int error, result = 0; if (is_set || !sn->rpcb_is_af_local) flags = RPC_TASK_SOFTCONN; msg->rpc_resp = &result; error = rpc_call_sync(clnt, msg, flags); if (error < 0) { dprintk("RPC: failed to contact local rpcbind " "server (errno %d).\n", -error); return error; } if (!result) return -EACCES; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever7070.71%480.00%
Trond Myklebust2929.29%120.00%
Total99100.00%5100.00%

/** * rpcb_register - set or unset a port registration with the local rpcbind svc * @net: target network namespace * @prog: RPC program number to bind * @vers: RPC version number to bind * @prot: transport protocol to register * @port: port value to register * * Returns zero if the registration request was dispatched successfully * and the rpcbind daemon returned success. Otherwise, returns an errno * value that reflects the nature of the error (request could not be * dispatched, timed out, or rpcbind returned an error). * * RPC services invoke this function to advertise their contact * information via the system's rpcbind daemon. RPC services * invoke this function once for each [program, version, transport] * tuple they wish to advertise. * * Callers may also unregister RPC services that are no longer * available by setting the passed-in port to zero. This removes * all registered transports for [program, version] from the local * rpcbind database. * * This function uses rpcbind protocol version 2 to contact the * local rpcbind daemon. * * Registration works over both AF_INET and AF_INET6, and services * registered via this function are advertised as available for any * address. If the local rpcbind daemon is listening on AF_INET6, * services registered via this function will be advertised on * IN6ADDR_ANY (ie available for all AF_INET and AF_INET6 * addresses). */
int rpcb_register(struct net *net, u32 prog, u32 vers, int prot, unsigned short port) { struct rpcbind_args map = { .r_prog = prog, .r_vers = vers, .r_prot = prot, .r_port = port, }; struct rpc_message msg = { .rpc_argp = &map, }; struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); bool is_set = false; dprintk("RPC: %sregistering (%u, %u, %d, %u) with local " "rpcbind\n", (port ? "" : "un"), prog, vers, prot, port); msg.rpc_proc = &rpcb_procedures2[RPCBPROC_UNSET]; if (port != 0) { msg.rpc_proc = &rpcb_procedures2[RPCBPROC_SET]; is_set = true; } return rpcb_register_call(sn, sn->rpcb_local_clnt, &msg, is_set); }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever11175.51%350.00%
Stanislav Kinsbursky1912.93%233.33%
Trond Myklebust1711.56%116.67%
Total147100.00%6100.00%

/* * Fill in AF_INET family-specific arguments to register */
static int rpcb_register_inet4(struct sunrpc_net *sn, const struct sockaddr *sap, struct rpc_message *msg) { const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; struct rpcbind_args *map = msg->rpc_argp; unsigned short port = ntohs(sin->sin_port); bool is_set = false; int result; map->r_addr = rpc_sockaddr2uaddr(sap, GFP_KERNEL); dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with " "local rpcbind\n", (port ? "" : "un"), map->r_prog, map->r_vers, map->r_addr, map->r_netid); msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET]; if (port != 0) { msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET]; is_set = true; } result = rpcb_register_call(sn, sn->rpcb_local_clnt4, msg, is_set); kfree(map->r_addr); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever13683.95%555.56%
Trond Myklebust1911.73%222.22%
Stanislav Kinsbursky74.32%222.22%
Total162100.00%9100.00%

/* * Fill in AF_INET6 family-specific arguments to register */
static int rpcb_register_inet6(struct sunrpc_net *sn, const struct sockaddr *sap, struct rpc_message *msg) { const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; struct rpcbind_args *map = msg->rpc_argp; unsigned short port = ntohs(sin6->sin6_port); bool is_set = false; int result; map->r_addr = rpc_sockaddr2uaddr(sap, GFP_KERNEL); dprintk("RPC: %sregistering [%u, %u, %s, '%s'] with " "local rpcbind\n", (port ? "" : "un"), map->r_prog, map->r_vers, map->r_addr, map->r_netid); msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET]; if (port != 0) { msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET]; is_set = true; } result = rpcb_register_call(sn, sn->rpcb_local_clnt4, msg, is_set); kfree(map->r_addr); return result; }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever13683.95%555.56%
Trond Myklebust1911.73%222.22%
Stanislav Kinsbursky74.32%222.22%
Total162100.00%9100.00%


static int rpcb_unregister_all_protofamilies(struct sunrpc_net *sn, struct rpc_message *msg) { struct rpcbind_args *map = msg->rpc_argp; dprintk("RPC: unregistering [%u, %u, '%s'] with " "local rpcbind\n", map->r_prog, map->r_vers, map->r_netid); map->r_addr = ""; msg->rpc_proc = &rpcb_procedures4[RPCBPROC_UNSET]; return rpcb_register_call(sn, sn->rpcb_local_clnt4, msg, false); }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever6284.93%240.00%
Stanislav Kinsbursky79.59%240.00%
Trond Myklebust45.48%120.00%
Total73100.00%5100.00%

/** * rpcb_v4_register - set or unset a port registration with the local rpcbind * @net: target network namespace * @program: RPC program number of service to (un)register * @version: RPC version number of service to (un)register * @address: address family, IP address, and port to (un)register * @netid: netid of transport protocol to (un)register * * Returns zero if the registration request was dispatched successfully * and the rpcbind daemon returned success. Otherwise, returns an errno * value that reflects the nature of the error (request could not be * dispatched, timed out, or rpcbind returned an error). * * RPC services invoke this function to advertise their contact * information via the system's rpcbind daemon. RPC services * invoke this function once for each [program, version, address, * netid] tuple they wish to advertise. * * Callers may also unregister RPC services that are registered at a * specific address by setting the port number in @address to zero. * They may unregister all registered protocol families at once for * a service by passing a NULL @address argument. If @netid is "" * then all netids for [program, version, address] are unregistered. * * This function uses rpcbind protocol version 4 to contact the * local rpcbind daemon. The local rpcbind daemon must support * version 4 of the rpcbind protocol in order for these functions * to register a service successfully. * * Supported netids include "udp" and "tcp" for UDP and TCP over * IPv4, and "udp6" and "tcp6" for UDP and TCP over IPv6, * respectively. * * The contents of @address determine the address family and the * port to be registered. The usual practice is to pass INADDR_ANY * as the raw address, but specifying a non-zero address is also * supported by this API if the caller wishes to advertise an RPC * service on a specific network interface. * * Note that passing in INADDR_ANY does not create the same service * registration as IN6ADDR_ANY. The former advertises an RPC * service on any IPv4 address, but not on IPv6. The latter * advertises the service on all IPv4 and IPv6 addresses. */
int rpcb_v4_register(struct net *net, const u32 program, const u32 version, const struct sockaddr *address, const char *netid) { struct rpcbind_args map = { .r_prog = program, .r_vers = version, .r_netid = netid, .r_owner = RPCB_OWNER_STRING, }; struct rpc_message msg = { .rpc_argp = &map, }; struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); if (sn->rpcb_local_clnt4 == NULL) return -EPROTONOSUPPORT; if (address == NULL) return rpcb_unregister_all_protofamilies(sn, &msg); switch (address->sa_family) { case AF_INET: return rpcb_register_inet4(sn, address, &msg); case AF_INET6: return rpcb_register_inet6(sn, address, &msg); } return -EAFNOSUPPORT; }

Contributors

PersonTokensPropCommitsCommitProp
Chuck Lever12182.88%350.00%
Stanislav Kinsbursky2517.12%350.00%
Total146100.00%6100.00%


static struct rpc_task *rpcb_call_async(struct rpc_clnt *rpcb_clnt, struct rpcbind_args *map, struct rpc_procinfo *proc) { struct rpc_message msg = { .rpc_proc = proc, .rpc_argp = map, .rpc_resp = map, }; struct rpc_task_setup task_setup_data = { .rpc_client = rpcb_clnt, .rpc_message = &msg, .callback_ops = &rpcb_getport_ops, .callback_data = map, .flags = RPC_TASK_ASYNC | RPC_TASK_SOFTCONN, }; return rpc_run_task(&task_setup_data); }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust8497.67%266.67%
Chuck Lever22.33%133.33%
Total86100.00%3100.00%

/* * In the case where rpc clients have been cloned, we want to make * sure that we use the program number/version etc of the actual * owner of the xprt. To do so, we walk back up the tree of parents * to find whoever created the transport and/or whoever has the * autobind flag set. */
static struct rpc_clnt *rpcb_find_transport_owner(struct rpc_clnt *clnt) { struct rpc_clnt *parent = clnt->cl_parent; struct rpc_xprt_switch *xps = rcu_access_pointer(clnt->cl_xpi.xpi_xpswitch); while (parent != clnt) { if (rcu_access_pointer(parent->cl_xpi.xpi_xpswitch) != xps) break; if (clnt->cl_autobind) break; clnt = parent; parent = parent->cl_parent; } return clnt; }

Contributors

PersonTokensPropCommitsCommitProp
Trond Myklebust78100.00%3100.00%
Total78100.00%3100.00%

/** * rpcb_getport_async - obtain the port for a given RPC service on a given host * @task: task that is waiting for portmapper request * * This one can be called for an ongoing RPC request, and can be used in * an async (rpciod) context. */
void rpcb_getport_async(struct rpc_task *task) { struct rpc_clnt *clnt; struct rpc_procinfo *proc; u32 bind_version; struct rpc_xprt *xprt; struct rpc_clnt *rpcb_clnt; struct rpcbind_args *map; struct rpc_task *child; struct sockaddr_storage addr; struct sockaddr *sap = (struct sockaddr *)&addr; size_t salen; int status; rcu_read_lock(); clnt = rpcb_find_transport_owner(task->tk_client); rcu_read_unlock(); xprt = xprt_get(task->tk_xprt); dprintk("RPC: %5u %s(%s, %u, %u, %d)\n", task->tk_pid, __func__, xprt->servername, clnt->cl_prog, clnt->cl_vers, xprt->prot); /* Put self on the wait queue to ensure we get notified if * some other task is already attempting to bind the port */ rpc_sleep_on(&xprt->binding, task, NULL); if (xprt_test_and_set_binding(xprt)) { dprintk("RPC: %5u %s: waiting for another binder\n", task->tk_pid, __func__); xprt_put(xprt); return; } /* Someone else may have bound if we slept */ if (xprt_bound(xprt)) { status = 0; dprintk("RPC: %5u %s: already bound\n", task->tk_pid, __func__); goto bailout_nofree; } /* Parent transport's destination address */ salen = rpc_peeraddr(clnt, sap, sizeof(addr)); /* Don't ever use rpcbind v2 for AF_INET6 requests */ switch (sap->sa_family) { case AF_INET: proc = rpcb_next_version[xprt->bind_index].rpc_proc; bind_version = rpcb_next_version[xprt->bind_index].rpc_vers; break; case AF_INET6: proc = rpcb_next_version6[xprt->bind_index].rpc_proc; bind_version = rpcb_next_version6[xprt->bind_index].rpc_vers; break; default: status = -EAFNOSUPPORT; dprintk("RPC: %5u %s: bad address family\n", task->tk_pid, __func__); goto bailout_nofree; } if (proc == NULL) { xprt->bind_index = 0; status = -EPFNOSUPPORT; dprintk("RPC: %5u %s: no more getport versions available\n", task->tk_pid, __func__); goto bailout_nofree; } dprintk("RPC: %5u %s: trying rpcbind version %u\n", task->tk_pid, __func__, bind_version); rpcb_clnt = rpcb_create(xprt->xprt_net, clnt->cl_nodename, xprt->servername, sap, salen, xprt->prot, bind_version); if (IS_ERR(rpcb_clnt)) { status = PTR_ERR(rpcb_clnt); dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n", task->tk_pid, __func__, PTR_ERR(rpcb_clnt)); goto bailout_nofree; } map = kzalloc(sizeof(struct rpcbind_args), GFP_ATOMIC); if (!map) { status = -ENOMEM; dprintk("RPC: %5u %s: no memory available\n", task->tk_pid, __func__); goto bailout_release_client; } map->r_prog = clnt->cl_prog; map->r_vers = clnt->cl_vers; map->r_prot = xprt->prot; map->r_port = 0; map->r_xprt = xprt; map->r_status = -EIO; switch (bind_version) { case RPCBVERS_4: case RPCBVERS_3: map->r_netid = xprt->address_strings[RPC_DISPLAY_NETID]; map->r_addr = rpc_sockaddr2uaddr(sap, GFP_ATOMIC); map->r_owner = ""; break; case RPCBVERS_2: map->r_addr = NULL; break; default: BUG(); } child = rpcb_call_async(rpcb_clnt, map, proc); rpc_release_client(rpcb_clnt); if (IS_ERR(child)) { /* rpcb_map_release() has freed the arguments */ dprintk("RPC: %5u %s: rpc_run_task failed\n", task->tk_pid, __func__); return; } xprt->stat.bind_count++; rpc_put_task(child); return; bailout_release_client: rpc_release_client(rpcb_clnt); bailout_nofree: rpcb_wake_rpcbind_waiters(xprt