aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2012-03-01 17:01:14 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-03-02 15:36:53 -0500
commit2e738fdce22f9a7edf20281fd2d768ef9785922e (patch)
tree634895e561ad33ac4be20adaf610f420e0de5ace
parent4e0038b6b246e4145fc4a53dca61a556d17bc52c (diff)
SUNRPC: Add API to acquire source address
NFSv4.0 clients must send endpoint information for their callback service to NFSv4.0 servers during their first contact with a server. Traditionally on Linux, user space provides the callback endpoint IP address via the "clientaddr=" mount option. During an NFSv4 migration event, it is possible that an FSID may be migrated to a destination server that is accessible via a different source IP address than the source server was. The client must update callback endpoint information on the destination server so that it can maintain leases and allow delegation. Without a new "clientaddr=" option from user space, however, the kernel itself must construct an appropriate IP address for the callback update. Provide an API in the RPC client for upper layer RPC consumers to acquire a source address for a remote. The mechanism used by the mount.nfs command is copied: set up a connected UDP socket to the designated remote, then scrape the source address off the socket. We are careful to select the correct network namespace when setting up the temporary UDP socket. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--include/linux/sunrpc/clnt.h1
-rw-r--r--net/sunrpc/clnt.c149
2 files changed, 150 insertions, 0 deletions
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index acd5502a8190..523547ecfee2 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -161,6 +161,7 @@ size_t rpc_max_payload(struct rpc_clnt *);
161void rpc_force_rebind(struct rpc_clnt *); 161void rpc_force_rebind(struct rpc_clnt *);
162size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t); 162size_t rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
163const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t); 163const char *rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
164int rpc_localaddr(struct rpc_clnt *, struct sockaddr *, size_t);
164 165
165size_t rpc_ntop(const struct sockaddr *, char *, const size_t); 166size_t rpc_ntop(const struct sockaddr *, char *, const size_t);
166size_t rpc_pton(struct net *, const char *, const size_t, 167size_t rpc_pton(struct net *, const char *, const size_t,
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index e39ace9a4e1d..7a4cb5fdc212 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -914,6 +914,155 @@ const char *rpc_peeraddr2str(struct rpc_clnt *clnt,
914} 914}
915EXPORT_SYMBOL_GPL(rpc_peeraddr2str); 915EXPORT_SYMBOL_GPL(rpc_peeraddr2str);
916 916
917static const struct sockaddr_in rpc_inaddr_loopback = {
918 .sin_family = AF_INET,
919 .sin_addr.s_addr = htonl(INADDR_ANY),
920};
921
922static const struct sockaddr_in6 rpc_in6addr_loopback = {
923 .sin6_family = AF_INET6,
924 .sin6_addr = IN6ADDR_ANY_INIT,
925};
926
927/*
928 * Try a getsockname() on a connected datagram socket. Using a
929 * connected datagram socket prevents leaving a socket in TIME_WAIT.
930 * This conserves the ephemeral port number space.
931 *
932 * Returns zero and fills in "buf" if successful; otherwise, a
933 * negative errno is returned.
934 */
935static int rpc_sockname(struct net *net, struct sockaddr *sap, size_t salen,
936 struct sockaddr *buf, int buflen)
937{
938 struct socket *sock;
939 int err;
940
941 err = __sock_create(net, sap->sa_family,
942 SOCK_DGRAM, IPPROTO_UDP, &sock, 1);
943 if (err < 0) {
944 dprintk("RPC: can't create UDP socket (%d)\n", err);
945 goto out;
946 }
947
948 switch (sap->sa_family) {
949 case AF_INET:
950 err = kernel_bind(sock,
951 (struct sockaddr *)&rpc_inaddr_loopback,
952 sizeof(rpc_inaddr_loopback));
953 break;
954 case AF_INET6:
955 err = kernel_bind(sock,
956 (struct sockaddr *)&rpc_in6addr_loopback,
957 sizeof(rpc_in6addr_loopback));
958 break;
959 default:
960 err = -EAFNOSUPPORT;
961 goto out;
962 }
963 if (err < 0) {
964 dprintk("RPC: can't bind UDP socket (%d)\n", err);
965 goto out_release;
966 }
967
968 err = kernel_connect(sock, sap, salen, 0);
969 if (err < 0) {
970 dprintk("RPC: can't connect UDP socket (%d)\n", err);
971 goto out_release;
972 }
973
974 err = kernel_getsockname(sock, buf, &buflen);
975 if (err < 0) {
976 dprintk("RPC: getsockname failed (%d)\n", err);
977 goto out_release;
978 }
979
980 err = 0;
981 if (buf->sa_family == AF_INET6) {
982 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf;
983 sin6->sin6_scope_id = 0;
984 }
985 dprintk("RPC: %s succeeded\n", __func__);
986
987out_release:
988 sock_release(sock);
989out:
990 return err;
991}
992
993/*
994 * Scraping a connected socket failed, so we don't have a useable
995 * local address. Fallback: generate an address that will prevent
996 * the server from calling us back.
997 *
998 * Returns zero and fills in "buf" if successful; otherwise, a
999 * negative errno is returned.
1000 */
1001static int rpc_anyaddr(int family, struct sockaddr *buf, size_t buflen)
1002{
1003 switch (family) {
1004 case AF_INET:
1005 if (buflen < sizeof(rpc_inaddr_loopback))
1006 return -EINVAL;
1007 memcpy(buf, &rpc_inaddr_loopback,
1008 sizeof(rpc_inaddr_loopback));
1009 break;
1010 case AF_INET6:
1011 if (buflen < sizeof(rpc_in6addr_loopback))
1012 return -EINVAL;
1013 memcpy(buf, &rpc_in6addr_loopback,
1014 sizeof(rpc_in6addr_loopback));
1015 default:
1016 dprintk("RPC: %s: address family not supported\n",
1017 __func__);
1018 return -EAFNOSUPPORT;
1019 }
1020 dprintk("RPC: %s: succeeded\n", __func__);
1021 return 0;
1022}
1023
1024/**
1025 * rpc_localaddr - discover local endpoint address for an RPC client
1026 * @clnt: RPC client structure
1027 * @buf: target buffer
1028 * @buflen: size of target buffer, in bytes
1029 *
1030 * Returns zero and fills in "buf" and "buflen" if successful;
1031 * otherwise, a negative errno is returned.
1032 *
1033 * This works even if the underlying transport is not currently connected,
1034 * or if the upper layer never previously provided a source address.
1035 *
1036 * The result of this function call is transient: multiple calls in
1037 * succession may give different results, depending on how local
1038 * networking configuration changes over time.
1039 */
1040int rpc_localaddr(struct rpc_clnt *clnt, struct sockaddr *buf, size_t buflen)
1041{
1042 struct sockaddr_storage address;
1043 struct sockaddr *sap = (struct sockaddr *)&address;
1044 struct rpc_xprt *xprt;
1045 struct net *net;
1046 size_t salen;
1047 int err;
1048
1049 rcu_read_lock();
1050 xprt = rcu_dereference(clnt->cl_xprt);
1051 salen = xprt->addrlen;
1052 memcpy(sap, &xprt->addr, salen);
1053 net = get_net(xprt->xprt_net);
1054 rcu_read_unlock();
1055
1056 rpc_set_port(sap, 0);
1057 err = rpc_sockname(net, sap, salen, buf, buflen);
1058 put_net(net);
1059 if (err != 0)
1060 /* Couldn't discover local address, return ANYADDR */
1061 return rpc_anyaddr(sap->sa_family, buf, buflen);
1062 return 0;
1063}
1064EXPORT_SYMBOL_GPL(rpc_localaddr);
1065
917void 1066void
918rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize) 1067rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize)
919{ 1068{