From: Chuck Lever <[email protected]>
The rpcbind versions 3 and 4 SET and UNSET procedures use the same
arguments as the GETADDR procedure.
While definitely a bug, this hasn't been a problem so far since the
kernel hasn't used version 3 or 4 SET and UNSET. But this will change
in just a moment.
Signed-off-by: Chuck Lever <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
---
net/sunrpc/rpcb_clnt.c | 12 ++++++++----
1 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 24e93e0..0021fad 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -426,6 +426,10 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)
map->r_status = status;
}
+/*
+ * XDR functions for rpcbind
+ */
+
static int rpcb_encode_mapping(struct rpc_rqst *req, __be32 *p,
struct rpcbind_args *rpcb)
{
@@ -581,14 +585,14 @@ static struct rpc_procinfo rpcb_procedures2[] = {
};
static struct rpc_procinfo rpcb_procedures3[] = {
- PROC(SET, mapping, set),
- PROC(UNSET, mapping, set),
+ PROC(SET, getaddr, set),
+ PROC(UNSET, getaddr, set),
PROC(GETADDR, getaddr, getaddr),
};
static struct rpc_procinfo rpcb_procedures4[] = {
- PROC(SET, mapping, set),
- PROC(UNSET, mapping, set),
+ PROC(SET, getaddr, set),
+ PROC(UNSET, getaddr, set),
PROC(GETADDR, getaddr, getaddr),
PROC(GETVERSADDR, getaddr, getaddr),
};
--
1.5.5.rc1
From: Chuck Lever <[email protected]>
Clean up: Callers that required a privileged source port now use
rpcb_create_local(), so we can remove the @privileged argument from
rpcb_create().
Signed-off-by: Chuck Lever <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
---
net/sunrpc/rpcb_clnt.c | 12 +++++-------
1 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 35c1ded..691bd21 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -140,8 +140,7 @@ static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr,
}
static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
- size_t salen, int proto, u32 version,
- int privileged)
+ size_t salen, int proto, u32 version)
{
struct rpc_create_args args = {
.protocol = proto,
@@ -151,7 +150,8 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
.program = &rpcb_program,
.version = version,
.authflavor = RPC_AUTH_UNIX,
- .flags = RPC_CLNT_CREATE_NOPING,
+ .flags = (RPC_CLNT_CREATE_NOPING |
+ RPC_CLNT_CREATE_NONPRIVPORT),
};
switch (srvaddr->sa_family) {
@@ -165,8 +165,6 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
return NULL;
}
- if (!privileged)
- args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
return rpc_create(&args);
}
@@ -255,7 +253,7 @@ int rpcb_getport_sync(struct sockaddr_in *sin, u32 prog, u32 vers, int prot)
__func__, NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
rpcb_clnt = rpcb_create(NULL, (struct sockaddr *)sin,
- sizeof(*sin), prot, RPCBVERS_2, 0);
+ sizeof(*sin), prot, RPCBVERS_2);
if (IS_ERR(rpcb_clnt))
return PTR_ERR(rpcb_clnt);
@@ -365,7 +363,7 @@ void rpcb_getport_async(struct rpc_task *task)
task->tk_pid, __func__, bind_version);
rpcb_clnt = rpcb_create(clnt->cl_server, sap, salen, xprt->prot,
- bind_version, 0);
+ bind_version);
if (IS_ERR(rpcb_clnt)) {
status = PTR_ERR(rpcb_clnt);
dprintk("RPC: %5u %s: rpcb_create failed, error %ld\n",
--
1.5.5.rc1
From: Chuck Lever <[email protected]>
rpcbind version 4 registration will reuse part of rpcb_register, so just
split it out into a separate function now.
Signed-off-by: Chuck Lever <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
---
net/sunrpc/rpcb_clnt.c | 48 ++++++++++++++++++++++++++++++------------------
1 files changed, 30 insertions(+), 18 deletions(-)
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 691bd21..8b75c30 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -168,6 +168,30 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
return rpc_create(&args);
}
+static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
+ u32 version, struct rpc_message *msg,
+ int *result)
+{
+ struct rpc_clnt *rpcb_clnt;
+ int error = 0;
+
+ *result = 0;
+
+ rpcb_clnt = rpcb_create_local(addr, addrlen, version);
+ if (!IS_ERR(rpcb_clnt)) {
+ error = rpc_call_sync(rpcb_clnt, msg, 0);
+ rpc_shutdown_client(rpcb_clnt);
+ } else
+ error = PTR_ERR(rpcb_clnt);
+
+ if (error < 0)
+ printk(KERN_WARNING "RPC: failed to contact local rpcbind "
+ "server (errno %d).\n", -error);
+ dprintk("RPC: registration status %d/%d\n", error, *result);
+
+ return error;
+}
+
/**
* rpcb_register - set or unset a port registration with the local rpcbind svc
* @prog: RPC program number to bind
@@ -189,33 +213,21 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
.r_port = port,
};
struct rpc_message msg = {
- .rpc_proc = &rpcb_procedures2[port ?
- RPCBPROC_SET : RPCBPROC_UNSET],
.rpc_argp = &map,
.rpc_resp = okay,
};
- struct rpc_clnt *rpcb_clnt;
- int error = 0;
dprintk("RPC: %sregistering (%u, %u, %d, %u) with local "
"rpcbind\n", (port ? "" : "un"),
prog, vers, prot, port);
- rpcb_clnt = rpcb_create_local((struct sockaddr *)&rpcb_inaddr_loopback,
- sizeof(rpcb_inaddr_loopback),
- RPCBVERS_2);
- if (!IS_ERR(rpcb_clnt)) {
- error = rpc_call_sync(rpcb_clnt, &msg, 0);
- rpc_shutdown_client(rpcb_clnt);
- } else
- error = PTR_ERR(rpcb_clnt);
+ msg.rpc_proc = &rpcb_procedures2[RPCBPROC_UNSET];
+ if (port)
+ msg.rpc_proc = &rpcb_procedures2[RPCBPROC_SET];
- if (error < 0)
- printk(KERN_WARNING "RPC: failed to contact local rpcbind "
- "server (errno %d).\n", -error);
- dprintk("RPC: registration status %d/%d\n", error, *okay);
-
- return error;
+ return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
+ sizeof(rpcb_inaddr_loopback),
+ RPCBVERS_2, &msg, okay);
}
/**
--
1.5.5.rc1
From: Chuck Lever <[email protected]>
Add rpcb_create_local() for use by rpcb_register() and upcoming IPv6
registration functions.
Ensure any errors encountered by rpcb_create_local() are properly
reported.
We can also use a statically allocated constant loopback socket address
instead of one allocated on the stack and initialized every time the
function is called.
Signed-off-by: Chuck Lever <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
---
net/sunrpc/rpcb_clnt.c | 42 +++++++++++++++++++++++++++++++-----------
1 files changed, 31 insertions(+), 11 deletions(-)
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 0021fad..35c1ded 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -116,6 +116,29 @@ static void rpcb_map_release(void *data)
kfree(map);
}
+static const struct sockaddr_in rpcb_inaddr_loopback = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
+ .sin_port = htons(RPCBIND_PORT),
+};
+
+static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr,
+ size_t addrlen, u32 version)
+{
+ struct rpc_create_args args = {
+ .protocol = XPRT_TRANSPORT_UDP,
+ .address = addr,
+ .addrsize = addrlen,
+ .servername = "localhost",
+ .program = &rpcb_program,
+ .version = version,
+ .authflavor = RPC_AUTH_UNIX,
+ .flags = RPC_CLNT_CREATE_NOPING,
+ };
+
+ return rpc_create(&args);
+}
+
static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
size_t salen, int proto, u32 version,
int privileged)
@@ -161,10 +184,6 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
*/
int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{
- struct sockaddr_in sin = {
- .sin_family = AF_INET,
- .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
- };
struct rpcbind_args map = {
.r_prog = prog,
.r_vers = vers,
@@ -184,14 +203,15 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
"rpcbind\n", (port ? "" : "un"),
prog, vers, prot, port);
- rpcb_clnt = rpcb_create("localhost", (struct sockaddr *) &sin,
- sizeof(sin), XPRT_TRANSPORT_UDP, RPCBVERS_2, 1);
- if (IS_ERR(rpcb_clnt))
- return PTR_ERR(rpcb_clnt);
+ rpcb_clnt = rpcb_create_local((struct sockaddr *)&rpcb_inaddr_loopback,
+ sizeof(rpcb_inaddr_loopback),
+ RPCBVERS_2);
+ if (!IS_ERR(rpcb_clnt)) {
+ error = rpc_call_sync(rpcb_clnt, &msg, 0);
+ rpc_shutdown_client(rpcb_clnt);
+ } else
+ error = PTR_ERR(rpcb_clnt);
- error = rpc_call_sync(rpcb_clnt, &msg, 0);
-
- rpc_shutdown_client(rpcb_clnt);
if (error < 0)
printk(KERN_WARNING "RPC: failed to contact local rpcbind "
"server (errno %d).\n", -error);
--
1.5.5.rc1
From: Chuck Lever <[email protected]>
Introduce a new API to register RPC services on IPv6 interfaces to allow
the NFS server and lockd to advertise on IPv6 networks.
Unlike rpcb_register(), the new rpcb_v4_register() function uses rpcbind
protocol version 4 to contact the local rpcbind daemon. The version 4
SET/UNSET procedures allow services to register address families besides
AF_INET, register at specific network interfaces, and register transport
protocols besides UDP and TCP. All of this functionality is exposed via
the new rpcb_v4_register() kernel API.
A user-space rpcbind daemon implementation that supports version 4 of the
rpcbind protocol is required in order to make use of this new API.
Note that rpcbind version 3 is sufficient to support the new rpcbind
facilities listed above, but most extant implementations use version 4.
Signed-off-by: Chuck Lever <[email protected]>
Signed-off-by: J. Bruce Fields <[email protected]>
---
include/linux/sunrpc/clnt.h | 3 +
net/sunrpc/rpcb_clnt.c | 178 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 177 insertions(+), 4 deletions(-)
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 764fd4c..e5bfe01 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -125,6 +125,9 @@ void rpc_shutdown_client(struct rpc_clnt *);
void rpc_release_client(struct rpc_clnt *);
int rpcb_register(u32, u32, int, unsigned short, int *);
+int rpcb_v4_register(const u32 program, const u32 version,
+ const struct sockaddr *address,
+ const char *netid, int *result);
int rpcb_getport_sync(struct sockaddr_in *, u32, u32, int);
void rpcb_getport_async(struct rpc_task *);
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 8b75c30..24db2b4 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -87,6 +87,7 @@ struct rpcbind_args {
static struct rpc_procinfo rpcb_procedures2[];
static struct rpc_procinfo rpcb_procedures3[];
+static struct rpc_procinfo rpcb_procedures4[];
struct rpcb_info {
u32 rpc_vers;
@@ -122,6 +123,12 @@ static const struct sockaddr_in rpcb_inaddr_loopback = {
.sin_port = htons(RPCBIND_PORT),
};
+static const struct sockaddr_in6 rpcb_in6addr_loopback = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
+ .sin6_port = htons(RPCBIND_PORT),
+};
+
static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr,
size_t addrlen, u32 version)
{
@@ -196,13 +203,38 @@ static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
* rpcb_register - set or unset a port registration with the local rpcbind svc
* @prog: RPC program number to bind
* @vers: RPC version number to bind
- * @prot: transport protocol to use to make this request
+ * @prot: transport protocol to register
* @port: port value to register
- * @okay: result code
+ * @okay: OUT: result code
+ *
+ * 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.
+ *
+ * Returns zero if the registration request was dispatched
+ * successfully and a reply was received. The rpcbind daemon's
+ * boolean result code is stored in *okay.
*
- * port == 0 means unregister, port != 0 means register.
+ * Returns an errno value and sets *result to zero if there was
+ * some problem that prevented the rpcbind request from being
+ * dispatched, or if the rpcbind daemon did not respond within
+ * the timeout.
*
- * This routine supports only rpcbind version 2.
+ * 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(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{
@@ -230,6 +262,144 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
RPCBVERS_2, &msg, okay);
}
+/*
+ * Fill in AF_INET family-specific arguments to register
+ */
+static int rpcb_register_netid4(struct sockaddr_in *address_to_register,
+ struct rpc_message *msg)
+{
+ struct rpcbind_args *map = msg->rpc_argp;
+ unsigned short port = ntohs(address_to_register->sin_port);
+ char buf[32];
+
+ /* Construct AF_INET universal address */
+ snprintf(buf, sizeof(buf),
+ NIPQUAD_FMT".%u.%u",
+ NIPQUAD(address_to_register->sin_addr.s_addr),
+ port >> 8, port & 0xff);
+ map->r_addr = buf;
+
+ 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)
+ msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET];
+
+ return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
+ sizeof(rpcb_inaddr_loopback),
+ RPCBVERS_4, msg, msg->rpc_resp);
+}
+
+/*
+ * Fill in AF_INET6 family-specific arguments to register
+ */
+static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
+ struct rpc_message *msg)
+{
+ struct rpcbind_args *map = msg->rpc_argp;
+ unsigned short port = ntohs(address_to_register->sin6_port);
+ char buf[64];
+
+ /* Construct AF_INET6 universal address */
+ snprintf(buf, sizeof(buf),
+ NIP6_FMT".%u.%u",
+ NIP6(address_to_register->sin6_addr),
+ port >> 8, port & 0xff);
+ map->r_addr = buf;
+
+ 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)
+ msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET];
+
+ return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback,
+ sizeof(rpcb_in6addr_loopback),
+ RPCBVERS_4, msg, msg->rpc_resp);
+}
+
+/**
+ * rpcb_v4_register - set or unset a port registration with the local rpcbind
+ * @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
+ * @result: result code from rpcbind RPC call
+ *
+ * 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 no longer
+ * available by setting the port number in the passed-in address
+ * to zero. Callers pass a netid of "" to unregister all
+ * transport netids associated with [program, version, address].
+ *
+ * Returns zero if the registration request was dispatched
+ * successfully and a reply was received. The rpcbind daemon's
+ * result code is stored in *result.
+ *
+ * Returns an errno value and sets *result to zero if there was
+ * some problem that prevented the rpcbind request from being
+ * dispatched, or if the rpcbind daemon did not respond within
+ * the timeout.
+ *
+ * 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(const u32 program, const u32 version,
+ const struct sockaddr *address, const char *netid,
+ int *result)
+{
+ 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,
+ .rpc_resp = result,
+ };
+
+ *result = 0;
+
+ switch (address->sa_family) {
+ case AF_INET:
+ return rpcb_register_netid4((struct sockaddr_in *)address,
+ &msg);
+ case AF_INET6:
+ return rpcb_register_netid6((struct sockaddr_in6 *)address,
+ &msg);
+ }
+
+ return -EAFNOSUPPORT;
+}
+
/**
* rpcb_getport_sync - obtain the port for an RPC service on a given host
* @sin: address of remote peer
--
1.5.5.rc1