Apparently a lot of people need to disable IPv6 completely on their distributor-built systems, which have CONFIG_IPV6_MODULE enabled at build time. They do this by blacklisting the ipv6.ko module. This causes the creation of the NFSv4 callback service listener to fail if CONFIG_IPV6_MODULE is set, but the module cannot be loaded. Now that the kernel's PF_INET6 RPC listeners are completely separate from PF_INET listeners, we can always start PF_INET. Then the NFS client can try to start a PF_INET6 listener, but it isn't required to be available. Note this has the added benefit that NFS callbacks from AF_INET6 servers will never come from AF_INET remotes. We no longer have to worry about matching mapped IPv4 addresses to AF_INET when comparing addresses. Signed-off-by: Chuck Lever <chuck.lever@xxxxxxxxxx> --- fs/nfs/callback.c | 12 +++++++++ fs/nfs/callback.h | 1 + fs/nfs/client.c | 69 ++++++++++++++++++++++------------------------------ fs/nfs/nfs4state.c | 10 ++++++-- 4 files changed, 50 insertions(+), 42 deletions(-) diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 0ef47df..a886e69 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -38,6 +38,7 @@ static struct svc_program nfs4_callback_program; unsigned int nfs_callback_set_tcpport; unsigned short nfs_callback_tcpport; +unsigned short nfs_callback_tcpport6; static const int nfs_set_port_min = 0; static const int nfs_set_port_max = 65535; @@ -119,6 +120,17 @@ int nfs_callback_up(void) dprintk("NFS: Callback listener port = %u (af %u)\n", nfs_callback_tcpport, PF_INET); +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + ret = svc_create_xprt(serv, "tcp", PF_INET6, + nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); + if (ret > 0) { + nfs_callback_tcpport6 = ret; + dprintk("NFS: Callback listener port = %u (af %u)\n", + nfs_callback_tcpport6, PF_INET6); + } else if (ret != -EAFNOSUPPORT) + goto out_err; +#endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ + nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); if (IS_ERR(nfs_callback_info.rqst)) { ret = PTR_ERR(nfs_callback_info.rqst); diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index bb25d21..e110e28 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -72,5 +72,6 @@ extern void nfs_callback_down(void); extern unsigned int nfs_callback_set_tcpport; extern unsigned short nfs_callback_tcpport; +extern unsigned short nfs_callback_tcpport6; #endif /* __LINUX_FS_NFS_CALLBACK_H */ diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 9b728f3..d64f0ec 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -223,54 +223,43 @@ void nfs_put_client(struct nfs_client *clp) } } -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static const struct in6_addr *nfs_map_ipv4_addr(const struct sockaddr *sa, struct in6_addr *addr_mapped) +static int nfs_cmp_addr4(const struct sockaddr *sap1, + const struct sockaddr *sap2) { - switch (sa->sa_family) { - default: - return NULL; - case AF_INET6: - return &((const struct sockaddr_in6 *)sa)->sin6_addr; - break; - case AF_INET: - ipv6_addr_set_v4mapped(((const struct sockaddr_in *)sa)->sin_addr.s_addr, - addr_mapped); - return addr_mapped; - } + const struct sockaddr_in *sin1 = (const struct sockaddr_in *)sap1; + const struct sockaddr_in *sin2 = (const struct sockaddr_in *)sap2; + return sin1->sin_addr.s_addr == sin2->sin_addr.s_addr; } -static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, - const struct sockaddr *sa2) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static int nfs_cmp_addr6(const struct sockaddr *sap1, + const struct sockaddr *sap2) { - const struct in6_addr *addr1; - const struct in6_addr *addr2; - struct in6_addr addr1_mapped; - struct in6_addr addr2_mapped; - - addr1 = nfs_map_ipv4_addr(sa1, &addr1_mapped); - if (likely(addr1 != NULL)) { - addr2 = nfs_map_ipv4_addr(sa2, &addr2_mapped); - if (likely(addr2 != NULL)) - return ipv6_addr_equal(addr1, addr2); - } - return 0; + const struct sockaddr_in6 *sin1 = (const struct sockaddr_in6 *)sap1; + const struct sockaddr_in6 *sin2 = (const struct sockaddr_in6 *)sap2; + return ipv6_addr_equal(&sin1->sin6_addr, &sin2->sin6_addr); } -#else -static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1, - const struct sockaddr_in *sa2) +#else /* !(CONFIG_IPV6 || CONFIG_IPV6_MODULE) */ +static int nfs_cmp_addr6(const struct sockaddr *sap1, + const struct sockaddr *sap2) { - return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr; + return 0; } +#endif /* !(CONFIG_IPV6 || CONFIG_IPV6_MODULE) */ -static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1, - const struct sockaddr *sa2) +static int nfs_cmp_addr(const struct sockaddr *sap1, + const struct sockaddr *sap2) { - if (unlikely(sa1->sa_family != AF_INET || sa2->sa_family != AF_INET)) - return 0; - return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1, - (const struct sockaddr_in *)sa2); + if (sap1->sa_family == sap2->sa_family) { + switch (sap1->sa_family) { + case AF_INET: + return nfs_cmp_addr4(sap1, sap2); + case AF_INET6: + return nfs_cmp_addr6(sap1, sap2); + } + } + return 0; } -#endif /* * Find a client by IP address and protocol version @@ -293,7 +282,7 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion) continue; /* Match only the IP address, not the port number */ - if (!nfs_sockaddr_match_ipaddr(addr, clap)) + if (!nfs_cmp_addr(addr, clap)) continue; atomic_inc(&clp->cl_count); @@ -326,7 +315,7 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp) continue; /* Match only the IP address, not the port number */ - if (!nfs_sockaddr_match_ipaddr(sap, clap)) + if (!nfs_cmp_addr(sap, clap)) continue; atomic_inc(&clp->cl_count); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 2022fe4..0298e90 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -62,8 +62,14 @@ static LIST_HEAD(nfs4_clientid_list); static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) { - int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, - nfs_callback_tcpport, cred); + unsigned short port; + int status; + + port = nfs_callback_tcpport; + if (clp->cl_addr.ss_family == AF_INET6) + port = nfs_callback_tcpport6; + + status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred); if (status == 0) status = nfs4_proc_setclientid_confirm(clp, cred); if (status == 0) -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html