SUNRPC: add support for IPv6 to the kernel's rpcbind client
authorChuck Lever <chuck.lever@oracle.com>
Mon, 6 Aug 2007 15:57:18 +0000 (11:57 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 9 Oct 2007 21:16:02 +0000 (17:16 -0400)
Prepare for adding IPv6 support to the RPC client by adding IPv6
capabilities to rpcbind.  Note that this is support on the query side
only; registering IPv6 addresses with the local portmapper will come
later.

Note we have to take care not to fall back to using version 2 of the
rpcbind protocol if we're dealing with IPv6 address.  Version 2 doesn't
support IPv6 at all.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Cc: Aurelien Charbon <aurelien.charbon@ext.bull.net>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
net/sunrpc/rpcb_clnt.c

index a9d2cdc..7db75e1 100644 (file)
@@ -16,6 +16,8 @@
 
 #include <linux/types.h>
 #include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 
@@ -137,10 +139,13 @@ struct rpcbind_args {
 static struct rpc_procinfo rpcb_procedures2[];
 static struct rpc_procinfo rpcb_procedures3[];
 
-static struct rpcb_info {
+struct rpcb_info {
        int                     rpc_vers;
        struct rpc_procinfo *   rpc_proc;
-} rpcb_next_version[];
+};
+
+static struct rpcb_info rpcb_next_version[];
+static struct rpcb_info rpcb_next_version6[];
 
 static void rpcb_getport_prepare(struct rpc_task *task, void *calldata)
 {
@@ -190,7 +195,17 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
                                   RPC_CLNT_CREATE_INTR),
        };
 
-       ((struct sockaddr_in *)srvaddr)->sin_port = htons(RPCBIND_PORT);
+       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 NULL;
+       }
+
        if (!privileged)
                args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
        return rpc_create(&args);
@@ -316,6 +331,7 @@ void rpcb_getport_async(struct rpc_task *task)
        struct rpc_task *child;
        struct sockaddr addr;
        int status;
+       struct rpcb_info *info;
 
        dprintk("RPC: %5u %s(%s, %u, %u, %d)\n",
                task->tk_pid, __FUNCTION__,
@@ -343,14 +359,30 @@ void rpcb_getport_async(struct rpc_task *task)
                goto bailout_nofree;
        }
 
-       if (rpcb_next_version[xprt->bind_index].rpc_proc == NULL) {
+       rpc_peeraddr(clnt, (void *)&addr, sizeof(addr));
+
+       /* Don't ever use rpcbind v2 for AF_INET6 requests */
+       switch (addr.sa_family) {
+       case AF_INET:
+               info = rpcb_next_version;
+               break;
+       case AF_INET6:
+               info = rpcb_next_version6;
+               break;
+       default:
+               status = -EAFNOSUPPORT;
+               dprintk("RPC: %5u %s: bad address family\n",
+                               task->tk_pid, __FUNCTION__);
+               goto bailout_nofree;
+       }
+       if (info[xprt->bind_index].rpc_proc == NULL) {
                xprt->bind_index = 0;
                status = -EACCES;       /* tell caller to try again later */
                dprintk("RPC: %5u %s: no more getport versions available\n",
                        task->tk_pid, __FUNCTION__);
                goto bailout_nofree;
        }
-       bind_version = rpcb_next_version[xprt->bind_index].rpc_vers;
+       bind_version = info[xprt->bind_index].rpc_vers;
 
        dprintk("RPC: %5u %s: trying rpcbind version %u\n",
                task->tk_pid, __FUNCTION__, bind_version);
@@ -373,7 +405,6 @@ void rpcb_getport_async(struct rpc_task *task)
                        sizeof(map->r_addr));
        map->r_owner = RPCB_OWNER_STRING;       /* ignored for GETADDR */
 
-       rpc_peeraddr(clnt, (void *)&addr, sizeof(addr));
        rpcb_clnt = rpcb_create(clnt->cl_server, &addr, xprt->prot, bind_version, 0);
        if (IS_ERR(rpcb_clnt)) {
                status = PTR_ERR(rpcb_clnt);
@@ -594,6 +625,14 @@ static struct rpcb_info rpcb_next_version[] = {
        { 0, NULL },
 };
 
+static struct rpcb_info rpcb_next_version6[] = {
+#ifdef CONFIG_SUNRPC_BIND34
+       { 4, &rpcb_procedures4[RPCBPROC_GETVERSADDR] },
+       { 3, &rpcb_procedures3[RPCBPROC_GETADDR] },
+#endif
+       { 0, NULL },
+};
+
 static struct rpc_version rpcb_version2 = {
        .number         = 2,
        .nrprocs        = RPCB_HIGHPROC_2,