]> git.decadent.org.uk Git - nfs-utils.git/commitdiff
Restore use of un-connected socket for UDP mount requests.
authorNeil Brown <neilb@suse.de>
Tue, 13 Mar 2007 03:39:49 +0000 (14:39 +1100)
committerNeil Brown <neilb@suse.de>
Tue, 13 Mar 2007 03:39:49 +0000 (14:39 +1100)
When connecting to an NFSv4 server we need to find out IP address
as it would be seen by the server, to register an address for
callbacks.
This is most easily done by connecting the socket to the
servers address and then getting the address of our endpoint.

However with a connected UDP socket, replies that come from a
different IP address - as can happen with non-Linux multi-homed
servers - will be rejected.

So if we connected our UDP socket, we need to be sure to
disconnect it before using it.

This patch adds an option to get_socket to say if we want it
connected or not and, in the case where we do, we disconnect
a UDP socket after the connection information has been used.

Also clean up the error handling in clnt_ping which was getting
clumsy.

support/include/conn.h
support/nfs/conn.c
utils/mount/nfsmount.c

index 1761dc4442ddd6e2cf0a1e4c520cf3fd8346614b..11f16ab1be812a95d832ca8a9a743900e97b4b11 100644 (file)
@@ -35,7 +35,7 @@ int clnt_ping(struct sockaddr_in *, const u_long, const u_long, const u_int,
              struct sockaddr_in *);
 u_long nfsvers_to_mnt(const u_long);
 u_long mntvers_to_nfs(const u_long);
              struct sockaddr_in *);
 u_long nfsvers_to_mnt(const u_long);
 u_long mntvers_to_nfs(const u_long);
-int get_socket(struct sockaddr_in *, u_int, int);
+int get_socket(struct sockaddr_in *, u_int, int, int);
 CLIENT * mnt_openclnt(clnt_addr_t *, int *);
 void mnt_closeclnt(CLIENT *, int);
 
 CLIENT * mnt_openclnt(clnt_addr_t *, int *);
 void mnt_closeclnt(CLIENT *, int);
 
index 89f767676b2aeb5f85d0b485c6865b6f5837c2bb..29dbb82c33dd102d3e90d0d0dcb223f7f7bc9c25 100644 (file)
@@ -49,7 +49,7 @@ u_long mntvers_to_nfs(const u_long vers)
  * RPC_ANYSOCK is returned which will cause 
  * the RPC code to create the socket instead. 
  */
  * RPC_ANYSOCK is returned which will cause 
  * the RPC code to create the socket instead. 
  */
-int get_socket(struct sockaddr_in *saddr, u_int p_prot, int resvp)
+int get_socket(struct sockaddr_in *saddr, u_int p_prot, int resvp, int conn)
 {
        int so, cc, type;
        struct sockaddr_in laddr;
 {
        int so, cc, type;
        struct sockaddr_in laddr;
@@ -98,7 +98,7 @@ int get_socket(struct sockaddr_in *saddr, u_int p_prot, int resvp)
                        return RPC_ANYSOCK;
                }
        }
                        return RPC_ANYSOCK;
                }
        }
-       if (type == SOCK_STREAM || type == SOCK_DGRAM) {
+       if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
                cc = connect(so, (struct sockaddr *)saddr, namelen);
                if (cc < 0) {
                        rpc_createerr.cf_stat = RPC_SYSTEMERROR;
                cc = connect(so, (struct sockaddr *)saddr, namelen);
                if (cc < 0) {
                        rpc_createerr.cf_stat = RPC_SYSTEMERROR;
@@ -129,20 +129,37 @@ clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers,
        CLIENT *clnt=NULL;
        int sock, stat;
        static char clnt_res;
        CLIENT *clnt=NULL;
        int sock, stat;
        static char clnt_res;
+       struct sockaddr dissolve;
 
        rpc_createerr.cf_stat = stat = errno = 0;
 
        rpc_createerr.cf_stat = stat = errno = 0;
-       sock = get_socket(saddr, prot, FALSE);
-       if (sock == RPC_ANYSOCK && errno == ETIMEDOUT) {
-               /*
-                * TCP timeout. Bubble up the error to see 
-                * how it should be handled.
-                */
-               rpc_createerr.cf_stat = RPC_TIMEDOUT;
-               goto out_bad;
+       sock = get_socket(saddr, prot, FALSE, TRUE);
+       if (sock == RPC_ANYSOCK) {
+               if (errno == ETIMEDOUT) {
+                       /*
+                        * TCP timeout. Bubble up the error to see 
+                        * how it should be handled.
+                        */
+                       rpc_createerr.cf_stat = RPC_TIMEDOUT;
+               }
+               return 0;
+       }
+
+       if (caddr) {
+               /* Get the address of our end of this connection */
+               socklen_t len = sizeof(*caddr);
+               if (getsockname(sock, caddr, &len) != 0)
+                       caddr->sin_family = 0;
        }
 
        switch(prot) {
        case IPPROTO_UDP:
        }
 
        switch(prot) {
        case IPPROTO_UDP:
+               /* The socket is connected (so we could getsockname successfully),
+                * but some servers on multi-homed hosts reply from
+                * the wrong address, so if we stay connected, we lose the reply.
+                */
+               dissolve.sa_family = AF_UNSPEC;
+               connect(sock, &dissolve, sizeof(dissolve));
+
                clnt = clntudp_bufcreate(saddr, prog, vers,
                                         RETRY_TIMEOUT, &sock,
                                         RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
                clnt = clntudp_bufcreate(saddr, prog, vers,
                                         RETRY_TIMEOUT, &sock,
                                         RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
@@ -151,11 +168,11 @@ clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers,
                clnt = clnttcp_create(saddr, prog, vers, &sock,
                                      RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
                break;
                clnt = clnttcp_create(saddr, prog, vers, &sock,
                                      RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
                break;
-       default:
-               goto out_bad;
        }
        }
-       if (!clnt)
-               goto out_bad;
+       if (!clnt) {
+               close(sock);
+               return 0;
+       }
        memset(&clnt_res, 0, sizeof(clnt_res));
        stat = clnt_call(clnt, NULLPROC,
                         (xdrproc_t)xdr_void, (caddr_t)NULL,
        memset(&clnt_res, 0, sizeof(clnt_res));
        stat = clnt_call(clnt, NULLPROC,
                         (xdrproc_t)xdr_void, (caddr_t)NULL,
@@ -166,21 +183,12 @@ clnt_ping(struct sockaddr_in *saddr, const u_long prog, const u_long vers,
                rpc_createerr.cf_stat = stat;
        }
        clnt_destroy(clnt);
                rpc_createerr.cf_stat = stat;
        }
        clnt_destroy(clnt);
-       if (sock != -1) {
-               if (caddr) {
-                       /* Get the address of our end of this connection */
-                       socklen_t len = sizeof(*caddr);
-                       if (getsockname(sock, caddr, &len) != 0)
-                               caddr->sin_family = 0;
-               }
-               close(sock);
-       }
+       close(sock);
 
        if (stat == RPC_SUCCESS)
                return 1;
 
        if (stat == RPC_SUCCESS)
                return 1;
-
- out_bad:
-       return 0;
+       else
+               return 0;
 }
 
 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
 }
 
 CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
@@ -191,7 +199,7 @@ CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
 
        /* contact the mount daemon via TCP */
        mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
 
        /* contact the mount daemon via TCP */
        mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
-       *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE);
+       *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, TRUE, FALSE);
 
        switch (mnt_pmap->pm_prot) {
        case IPPROTO_UDP:
 
        switch (mnt_pmap->pm_prot) {
        case IPPROTO_UDP:
index b78daadec7f1563930cf23f4fb08c8674cdda7fe..2ad7b35fb149f027a2f2236c4e72b3517b20a869 100644 (file)
@@ -306,7 +306,7 @@ getport(
        enum clnt_stat stat;
 
        saddr->sin_port = htons (PMAPPORT);
        enum clnt_stat stat;
 
        saddr->sin_port = htons (PMAPPORT);
-       socket = get_socket(saddr, prot, FALSE);
+       socket = get_socket(saddr, prot, FALSE, FALSE);
 
        switch (prot) {
        case IPPROTO_UDP:
 
        switch (prot) {
        case IPPROTO_UDP: