You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/03/02 21:00:38 UTC

[incubator-nuttx] 02/03: fs/nfs: Support both IPv6 and TCP

This is an automated email from the ASF dual-hosted git repository.

gnutt pushed a commit to branch pr416
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 9ec8471ca7fde7cb6b2f8a6c5ab4d0e243522cdf
Author: Xiang Xiao <xi...@xiaomi.com>
AuthorDate: Sun Mar 1 17:24:29 2020 +0800

    fs/nfs: Support both IPv6 and TCP
    
    and correctly handle the retransmission
    
    Signed-off-by: Xiang Xiao <xi...@xiaomi.com>
    Change-Id: I4862dc84d036020784e72953ba788bcd3c1e0397
---
 fs/nfs/Kconfig                |   2 +-
 fs/nfs/nfs.h                  |   2 +-
 fs/nfs/nfs_mount.h            |   4 +-
 fs/nfs/nfs_util.c             |  13 +-
 fs/nfs/nfs_vfsops.c           | 112 +++++-------
 fs/nfs/rpc.h                  |   5 +-
 fs/nfs/rpc_clnt.c             | 394 +++++++++++++++++++++++-------------------
 include/nuttx/net/netconfig.h |   1 +
 8 files changed, 271 insertions(+), 262 deletions(-)

diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index 4a9effc..a25b3b5 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -6,7 +6,7 @@
 config NFS
 	bool "NFS client file system"
 	default n
-	depends on NET_UDP && NET_IPv4 && !DISABLE_MOUNTPOINT
+	depends on !DISABLE_MOUNTPOINT
 	select FS_READABLE
 	select FS_WRITABLE
 	---help---
diff --git a/fs/nfs/nfs.h b/fs/nfs/nfs.h
index a17750c..3f17e2e 100644
--- a/fs/nfs/nfs.h
+++ b/fs/nfs/nfs.h
@@ -61,7 +61,7 @@
 #define NFS_RETRANS        10             /* Num of retrans for soft mounts */
 #define NFS_WSIZE          8192           /* Def. write data size <= 8192 */
 #define NFS_RSIZE          8192           /* Def. read data size <= 8192 */
-#define NFS_READDIRSIZE    8192           /* Def. readdir size */
+#define NFS_READDIRSIZE    1024           /* Def. readdir size */
 
 /* Ideally, NFS_DIRBLKSIZ should be bigger, but I've seen servers with
  * broken NFS/ethernet drivers that won't work with anything bigger (Linux..)
diff --git a/fs/nfs/nfs_mount.h b/fs/nfs/nfs_mount.h
index 4bab615..4c9cf54 100644
--- a/fs/nfs/nfs_mount.h
+++ b/fs/nfs/nfs_mount.h
@@ -73,7 +73,7 @@ struct nfsmount
   char                      nm_path[90];      /* server's path of the directory being mounted */
   struct nfs_fattr          nm_fattr;         /* nfs file attribute cache */
   FAR struct rpcclnt       *nm_rpcclnt;       /* RPC state */
-  struct sockaddr           nm_nam;           /* Addr of server */
+  struct sockaddr_storage   nm_nam;           /* Addr of server */
   uint8_t                   nm_fhsize;        /* Size of root file handle (host order) */
   uint16_t                  nm_rsize;         /* Max size of read RPC */
   uint16_t                  nm_wsize;         /* Max size of write RPC */
@@ -87,8 +87,6 @@ struct nfsmount
 
   union
   {
-    struct rpc_call_pmap    pmap;
-    struct rpc_call_mount   mountd;
     struct rpc_call_create  create;
     struct rpc_call_lookup  lookup;
     struct rpc_call_read    read;
diff --git a/fs/nfs/nfs_util.c b/fs/nfs/nfs_util.c
index abfa796..166b661 100644
--- a/fs/nfs/nfs_util.c
+++ b/fs/nfs/nfs_util.c
@@ -149,18 +149,9 @@ int nfs_request(FAR struct nfsmount *nmp, int procnum,
 
   if (replyh.nfs_status != 0)
     {
-      if (fxdr_unsigned(uint32_t, replyh.nfs_status) > 32)
-        {
-          error = -EOPNOTSUPP;
-        }
-      else
-        {
-          /* NFS_ERRORS are the same as NuttX errno values */
+      /* NFS_ERRORS are the same as NuttX errno values */
 
-          error = -fxdr_unsigned(uint32_t, replyh.nfs_status);
-        }
-
-      return error;
+      return -fxdr_unsigned(uint32_t, replyh.nfs_status);
     }
 
   if (replyh.rh.rpc_verfi.authtype != 0)
diff --git a/fs/nfs/nfs_vfsops.c b/fs/nfs/nfs_vfsops.c
index fe5cbbe..7d74670 100644
--- a/fs/nfs/nfs_vfsops.c
+++ b/fs/nfs/nfs_vfsops.c
@@ -66,8 +66,6 @@
 #include <nuttx/fs/dirent.h>
 #include <nuttx/fs/fs.h>
 #include <nuttx/fs/nfs.h>
-#include <nuttx/net/udp.h>
-#include <nuttx/net/arp.h>
 #include <nuttx/net/netconfig.h>
 
 #include <net/if.h>
@@ -1634,16 +1632,20 @@ static void nfs_decode_args(FAR struct nfs_mount_parameters *nprmt,
 
   /* Get the maximum amount of data that can be transferred in one packet */
 
-  if ((argp->sotype == SOCK_DGRAM) != 0)
+  if (argp->sotype == SOCK_DGRAM)
     {
       maxio = NFS_MAXDGRAMDATA;
     }
   else
     {
-      ferr("ERROR: Only SOCK_DRAM is supported\n");
       maxio = NFS_MAXDATA;
     }
 
+  if (maxio > MAXBSIZE)
+    {
+      maxio = MAXBSIZE;
+    }
+
   /* Get the maximum amount of data that can be transferred in one write transfer */
 
   if ((argp->flags & NFSMNT_WSIZE) != 0 && argp->wsize > 0)
@@ -1664,11 +1666,6 @@ static void nfs_decode_args(FAR struct nfs_mount_parameters *nprmt,
       nprmt->wsize = maxio;
     }
 
-  if (nprmt->wsize > MAXBSIZE)
-    {
-      nprmt->wsize = MAXBSIZE;
-    }
-
   /* Get the maximum amount of data that can be transferred in one read transfer */
 
   if ((argp->flags & NFSMNT_RSIZE) != 0 && argp->rsize > 0)
@@ -1689,11 +1686,6 @@ static void nfs_decode_args(FAR struct nfs_mount_parameters *nprmt,
       nprmt->rsize = maxio;
     }
 
-  if (nprmt->rsize > MAXBSIZE)
-    {
-      nprmt->rsize = MAXBSIZE;
-    }
-
   /* Get the maximum amount of data that can be transferred in directory transfer */
 
   if ((argp->flags & NFSMNT_READDIRSIZE) != 0 && argp->readdirsize > 0)
@@ -1772,6 +1764,14 @@ static int nfs_bind(FAR struct inode *blkdriver, FAR const void *data,
       buflen = tmp;
     }
 
+  /* And consider the maximum size of a read dir transfer too */
+
+  tmp = SIZEOF_rpc_reply_readdir(nprmt.readdirsize);
+  if (tmp > buflen)
+    {
+      buflen = tmp;
+    }
+
   /* But don't let the buffer size exceed the MSS of the socket type.
    *
    * In the case where there are multiple network devices with different
@@ -1780,9 +1780,9 @@ static int nfs_bind(FAR struct inode *blkdriver, FAR const void *data,
    * that case.
    */
 
-  if (buflen > MIN_IPv4_UDP_MSS)
+  if (argp->sotype == SOCK_DGRAM && buflen > MIN_UDP_MSS)
     {
-      buflen = MIN_IPv4_UDP_MSS;
+      buflen = MIN_UDP_MSS;
     }
 
   /* Create an instance of the mountpt state structure */
@@ -1824,43 +1824,36 @@ static int nfs_bind(FAR struct inode *blkdriver, FAR const void *data,
   strncpy(nmp->nm_path, argp->path, 90);
   memcpy(&nmp->nm_nam, &argp->addr, argp->addrlen);
 
-  /* Set up the sockets and per-host congestion */
+  /* Create an instance of the rpc state structure */
 
-  if (argp->sotype == SOCK_DGRAM)
+  rpc = (FAR struct rpcclnt *)kmm_zalloc(sizeof(struct rpcclnt));
+  if (!rpc)
     {
-      /* Connection-less... connect now */
-
-      /* Create an instance of the rpc state structure */
-
-      rpc = (FAR struct rpcclnt *)kmm_zalloc(sizeof(struct rpcclnt));
-      if (!rpc)
-        {
-          ferr("ERROR: Failed to allocate rpc structure\n");
-          return -ENOMEM;
-        }
+      ferr("ERROR: Failed to allocate rpc structure\n");
+      return -ENOMEM;
+    }
 
-      finfo("Connecting\n");
+  finfo("Connecting\n");
 
-      /* Translate nfsmnt flags -> rpcclnt flags */
+  /* Translate nfsmnt flags -> rpcclnt flags */
 
-      rpc->rc_path       = nmp->nm_path;
-      rpc->rc_name       = &nmp->nm_nam;
-      rpc->rc_sotype     = argp->sotype;
-      rpc->rc_timeo      = nprmt.timeo;
-      rpc->rc_retry      = nprmt.retry;
+  rpc->rc_path        = nmp->nm_path;
+  rpc->rc_name        = &nmp->nm_nam;
+  rpc->rc_sotype      = argp->sotype;
+  rpc->rc_timeo       = nprmt.timeo;
+  rpc->rc_retry       = nprmt.retry;
 
-      nmp->nm_rpcclnt    = rpc;
+  nmp->nm_rpcclnt     = rpc;
 
-      error = rpcclnt_connect(nmp->nm_rpcclnt);
-      if (error != OK)
-        {
-          ferr("ERROR: nfs_connect failed: %d\n", error);
-          goto bad;
-        }
+  error = rpcclnt_connect(nmp->nm_rpcclnt);
+  if (error != OK)
+    {
+      ferr("ERROR: nfs_connect failed: %d\n", error);
+      goto bad;
     }
 
-  nmp->nm_fhsize         = nmp->nm_rpcclnt->rc_fhsize;
-  nmp->nm_fh             = &nmp->nm_rpcclnt->rc_fh;
+  nmp->nm_fhsize      = nmp->nm_rpcclnt->rc_fhsize;
+  nmp->nm_fh          = &nmp->nm_rpcclnt->rc_fh;
 
   /* Get the file sytem info */
 
@@ -1880,21 +1873,18 @@ static int nfs_bind(FAR struct inode *blkdriver, FAR const void *data,
   return OK;
 
 bad:
-  if (nmp)
-    {
-      /* Disconnect from the server */
+  /* Disconnect from the server */
 
-      if (nmp->nm_rpcclnt)
-        {
-          rpcclnt_disconnect(nmp->nm_rpcclnt);
-          kmm_free(nmp->nm_rpcclnt);
-        }
+  if (nmp->nm_rpcclnt)
+    {
+      rpcclnt_disconnect(nmp->nm_rpcclnt);
+      kmm_free(nmp->nm_rpcclnt);
+    }
 
-      /* Free connection-related resources */
+  /* Free connection-related resources */
 
-      nxsem_destroy(&nmp->nm_sem);
-      kmm_free(nmp);
-    }
+  nxsem_destroy(&nmp->nm_sem);
+  kmm_free(nmp);
 
   return error;
 }
@@ -1941,14 +1931,6 @@ static int nfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
       goto errout_with_semaphore;
     }
 
-  /* No open file... Umount the file system. */
-
-  error = rpcclnt_umount(nmp->nm_rpcclnt);
-  if (error)
-    {
-      ferr("ERROR: rpcclnt_umount failed: %d\n", error);
-    }
-
   /* Disconnect from the server */
 
   rpcclnt_disconnect(nmp->nm_rpcclnt);
@@ -1959,7 +1941,7 @@ static int nfs_unbind(FAR void *handle, FAR struct inode **blkdriver,
   kmm_free(nmp->nm_rpcclnt);
   kmm_free(nmp);
 
-  return error;
+  return OK;
 
 errout_with_semaphore:
   nfs_semgive(nmp);
diff --git a/fs/nfs/rpc.h b/fs/nfs/rpc.h
index 1139338..06daa96 100644
--- a/fs/nfs/rpc.h
+++ b/fs/nfs/rpc.h
@@ -146,7 +146,6 @@
 
 /* RPC definitions for the portmapper. */
 
-#define PMAPPORT         111
 #define PMAPPROG         100000
 #define PMAPVERS         2
 
@@ -450,12 +449,13 @@ struct rpcclnt
   uint8_t   rc_fhsize;        /* File size of the root directory */
   FAR char *rc_path;          /* Server's path of the mounted directory */
 
-  FAR struct sockaddr *rc_name;
+  FAR struct sockaddr_storage *rc_name;
   struct socket rc_so;        /* RPC socket */
 
   uint8_t   rc_sotype;        /* Type of socket */
   uint8_t   rc_timeo;         /* Timeout value (in deciseconds) */
   uint8_t   rc_retry;         /* Max retries */
+  uint32_t  rc_xid;           /* Transaction id */
 };
 
 /****************************************************************************
@@ -465,7 +465,6 @@ struct rpcclnt
 void rpcclnt_init(void);
 int  rpcclnt_connect(FAR struct rpcclnt *rpc);
 void rpcclnt_disconnect(FAR struct rpcclnt *rpc);
-int  rpcclnt_umount(FAR struct rpcclnt *rpc);
 int  rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
                      int version, FAR void *request, size_t reqlen,
                      FAR void *response, size_t resplen);
diff --git a/fs/nfs/rpc_clnt.c b/fs/nfs/rpc_clnt.c
index aa40e46..43f847f 100644
--- a/fs/nfs/rpc_clnt.c
+++ b/fs/nfs/rpc_clnt.c
@@ -81,12 +81,9 @@
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <queue.h>
-#include <time.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <debug.h>
 
@@ -143,13 +140,13 @@ static struct rpcstats rpcstats;
  * Private Function Prototypes
  ****************************************************************************/
 
+static int rpcclnt_socket(FAR struct rpcclnt *rpc, in_port_t rport);
 static int rpcclnt_send(FAR struct rpcclnt *rpc,
                         FAR void *call, int reqlen);
 static int rpcclnt_receive(FAR struct rpcclnt *rpc,
                            FAR void *reply, size_t resplen);
-static int rpcclnt_reply(FAR struct rpcclnt *rpc,
+static int rpcclnt_reply(FAR struct rpcclnt *rpc, uint32_t xid,
                          FAR void *reply, size_t resplen);
-static uint32_t rpcclnt_newxid(void);
 static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
                               uint32_t xid, int procid, int prog, int vers);
 
@@ -158,6 +155,132 @@ static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
  ****************************************************************************/
 
 /****************************************************************************
+ * Name: rpcclnt_socket
+ *
+ * Description:
+ *   Close(old), create, bind and connect the socket.
+ *
+ * Returned Value:
+ *   Returns zero on success or a (negative) errno value on failure.
+ *
+ ****************************************************************************/
+
+static int rpcclnt_socket(FAR struct rpcclnt *rpc, in_port_t rport)
+{
+  struct sockaddr_storage raddr;
+  struct sockaddr_storage laddr;
+  FAR in_port_t *lport;
+  in_port_t port = 1024;
+  struct timeval tv;
+  socklen_t addrlen;
+  int error;
+
+  /* Close the old socket */
+
+  psock_close(&rpc->rc_so);
+
+  /* Prepare the socket address */
+
+  memcpy(&raddr, rpc->rc_name, sizeof(raddr));
+
+  laddr.ss_family = raddr.ss_family;
+  memset(laddr.ss_data, 0, sizeof(laddr.ss_data));
+
+  if (raddr.ss_family == AF_INET6)
+    {
+      FAR struct sockaddr_in6 *sin;
+
+      addrlen = sizeof(struct sockaddr_in6);
+      if (rport != 0)
+        {
+          sin = (FAR struct sockaddr_in6 *)&raddr;
+          sin->sin6_port = htons(rport);
+        }
+
+      sin = (FAR struct sockaddr_in6 *)&laddr;
+      lport = &sin->sin6_port;
+    }
+  else
+    {
+      FAR struct sockaddr_in *sin;
+
+      addrlen = sizeof(struct sockaddr_in);
+      if (rport != 0)
+        {
+          sin = (FAR struct sockaddr_in *)&raddr;
+          sin->sin_port = htons(rport);
+        }
+
+      sin = (FAR struct sockaddr_in *)&laddr;
+      lport = &sin->sin_port;
+    }
+
+  /* Create the socket */
+
+  error = psock_socket(raddr.ss_family, rpc->rc_sotype, 0, &rpc->rc_so);
+  if (error < 0)
+    {
+      ferr("ERROR: psock_socket failed: %d", error);
+      return error;
+    }
+
+  /* Always set receive timeout to detect server crash and reconnect.
+   * Otherwise, we can get stuck in psock_receive forever.
+   */
+
+  tv.tv_sec  = rpc->rc_timeo / 10;
+  tv.tv_usec = (rpc->rc_timeo % 10) * 100000;
+
+  error = psock_setsockopt(&rpc->rc_so, SOL_SOCKET, SO_RCVTIMEO,
+                          (FAR const void *)&tv, sizeof(tv));
+  if (error < 0)
+    {
+      ferr("ERROR: psock_setsockopt failed: %d\n", error);
+      goto bad;
+    }
+
+  /* Some servers require that the client port be a reserved port
+   * number. We always allocate a reserved port, as this prevents
+   * filehandle disclosure through UDP port capture.
+   */
+
+  do
+    {
+      *lport = htons(--port);
+      error = psock_bind(&rpc->rc_so, (FAR struct sockaddr *)&laddr, addrlen);
+      if (error < 0)
+        {
+          ferr("ERROR: psock_bind failed: %d\n", error);
+        }
+    }
+  while (error == -EADDRINUSE && port >= 512);
+
+  if (error)
+    {
+      ferr("ERROR: psock_bind failed: %d\n", error);
+      goto bad;
+    }
+
+  /* Protocols that do not require connections could be optionally left
+   * unconnected.  That would allow servers to reply from a port other than
+   * the NFS_PORT.
+   */
+
+  error = psock_connect(&rpc->rc_so, (FAR struct sockaddr *)&raddr, addrlen);
+  if (error < 0)
+    {
+      ferr("ERROR: psock_connect to PMAP port failed: %d", error);
+      goto bad;
+    }
+
+  return OK;
+
+bad:
+  psock_close(&rpc->rc_so);
+  return error;
+}
+
+/****************************************************************************
  * Name: rpcclnt_send
  *
  * Description:
@@ -171,26 +294,36 @@ static void rpcclnt_fmtheader(FAR struct rpc_call_header *ch,
 static int rpcclnt_send(FAR struct rpcclnt *rpc,
                         FAR void *call, int reqlen)
 {
-  ssize_t nbytes;
+  uint32_t mark;
   int ret = OK;
 
+  /* Send the record marking(RM) for stream only */
+
+  if (rpc->rc_sotype == SOCK_STREAM)
+    {
+      mark = txdr_unsigned(0x80000000 | reqlen);
+      ret = psock_send(&rpc->rc_so, &mark, sizeof(mark), 0);
+      if (ret < 0)
+        {
+          ferr("ERROR: psock_send mark failed: %d\n", ret);
+          return ret;
+        }
+    }
+
   /* Send the call message
    *
    * On success, psock_send returns the number of bytes sent;
    * On failure, it returns a negated errno value.
    */
 
-  nbytes = psock_send(&rpc->rc_so, call, reqlen, 0);
-
-  if (nbytes < 0)
+  ret = psock_send(&rpc->rc_so, call, reqlen, 0);
+  if (ret < 0)
     {
-      /* psock_sendto failed */
-
-      ret = nbytes;
-      ferr("ERROR: psock_sendto failed: %d\n", ret);
+      ferr("ERROR: psock_send request failed: %d\n", ret);
+      return ret;
     }
 
-  return ret;
+  return OK;
 }
 
 /****************************************************************************
@@ -204,17 +337,45 @@ static int rpcclnt_send(FAR struct rpcclnt *rpc,
 static int rpcclnt_receive(FAR struct rpcclnt *rpc,
                            FAR void *reply, size_t resplen)
 {
-  ssize_t nbytes;
+  uint32_t mark;
   int error = 0;
 
-  nbytes = psock_recv(&rpc->rc_so, reply, resplen, 0);
-  if (nbytes < 0)
+  /* Receive the record marking(RM) for stream only */
+
+  if (rpc->rc_sotype == SOCK_STREAM)
+    {
+      error = psock_recv(&rpc->rc_so, &mark, sizeof(mark), 0);
+      if (error < 0)
+        {
+          ferr("ERROR: psock_recv mark failed: %d\n", error);
+          return error;
+        }
+
+      /* Limit the receive length to the marked value */
+
+      mark = fxdr_unsigned(uint32_t, mark);
+      if (!(mark & 0x80000000))
+        {
+          return -ENOSYS;
+        }
+
+      mark &= 0x7fffffff;
+      if (mark > resplen)
+        {
+          return -E2BIG;
+        }
+
+      resplen = mark;
+    }
+
+  error = psock_recv(&rpc->rc_so, reply, resplen, 0);
+  if (error < 0)
     {
-      error = nbytes;
-      ferr("ERROR: psock_recv failed: %d\n", error);
+      ferr("ERROR: psock_recv response failed: %d\n", error);
+      return error;
     }
 
-  return error;
+  return OK;
 }
 
 /****************************************************************************
@@ -225,11 +386,12 @@ static int rpcclnt_receive(FAR struct rpcclnt *rpc,
  *
  ****************************************************************************/
 
-static int rpcclnt_reply(FAR struct rpcclnt *rpc,
+static int rpcclnt_reply(FAR struct rpcclnt *rpc, uint32_t xid,
                          FAR void *reply, size_t resplen)
 {
   int error;
 
+retry:
   /* Get the next RPC reply from the socket */
 
   error = rpcclnt_receive(rpc, reply, resplen);
@@ -251,43 +413,15 @@ static int rpcclnt_reply(FAR struct rpcclnt *rpc,
           rpc_statistics(rpcinvalid);
           error = -EPROTO;
         }
-    }
-
-  return error;
-}
-
-/****************************************************************************
- * Name: rpcclnt_newxid
- *
- * Description:
- *   Get a new (non-zero) xid
- *
- ****************************************************************************/
-
-static uint32_t rpcclnt_newxid(void)
-{
-  static uint32_t rpcclnt_xid = 0;
-  static uint32_t rpcclnt_xid_touched = 0;
-
-  if ((rpcclnt_xid == 0) && (rpcclnt_xid_touched == 0))
-    {
-      srand(time(NULL));
-      rpcclnt_xid = rand();
-      rpcclnt_xid_touched = 1;
-    }
-  else
-    {
-      int xidp = 0;
-      do
+      else if (replyheader->rp_xid != txdr_unsigned(xid))
         {
-          xidp = rand();
+          ferr("ERROR: Different RPC XID returned\n");
+          rpc_statistics(rpcinvalid);
+          goto retry;
         }
-      while ((xidp % 256) == 0);
-
-      rpcclnt_xid += xidp;
     }
 
-  return rpcclnt_xid;
+  return error;
 }
 
 /****************************************************************************
@@ -357,9 +491,7 @@ void rpcclnt_init(void)
 int rpcclnt_connect(FAR struct rpcclnt *rpc)
 {
   int error;
-  FAR struct sockaddr *saddr;
-  struct sockaddr_in sin;
-  FAR struct sockaddr_in *sa;
+  int prot;
 
   union
   {
@@ -373,76 +505,18 @@ int rpcclnt_connect(FAR struct rpcclnt *rpc)
     struct rpc_reply_mount mdata;
   } response;
 
-  struct timeval tv;
-  uint16_t tport;
-
   finfo("Connecting\n");
 
   /* Create the socket */
 
-  saddr = rpc->rc_name;
-
-  error = psock_socket(saddr->sa_family, rpc->rc_sotype, IPPROTO_UDP, &rpc->rc_so);
+  error = rpcclnt_socket(rpc, 0);
   if (error < 0)
     {
-      ferr("ERROR: psock_socket failed: %d", error);
+      ferr("ERROR: rpcclnt_socket failed: %d", error);
       return error;
     }
 
-  /* Always set receive timeout to detect server crash and reconnect.
-   * Otherwise, we can get stuck in psock_receive forever.
-   */
-
-  tv.tv_sec  = rpc->rc_timeo / 10;
-  tv.tv_usec = (rpc->rc_timeo % 10) * 100000;
-
-  error = psock_setsockopt(&rpc->rc_so, SOL_SOCKET, SO_RCVTIMEO,
-                          (FAR const void *)&tv, sizeof(tv));
-  if (error < 0)
-    {
-      ferr("ERROR: psock_setsockopt failed: %d\n", error);
-      goto bad;
-    }
-
-  /* Some servers require that the client port be a reserved port
-   * number. We always allocate a reserved port, as this prevents
-   * filehandle disclosure through UDP port capture.
-   */
-
-  sin.sin_family      = AF_INET;
-  sin.sin_addr.s_addr = INADDR_ANY;
-  tport               = 1024;
-
-  do
-    {
-      tport--;
-      sin.sin_port = htons(tport);
-
-      error = psock_bind(&rpc->rc_so, (struct sockaddr *)&sin, sizeof(sin));
-      if (error < 0)
-        {
-          ferr("ERROR: psock_bind failed: %d\n", error);
-        }
-    }
-  while (error == -EADDRINUSE && tport > 1024 / 2);
-
-  if (error)
-    {
-      ferr("ERROR: psock_bind failed: %d\n", error);
-      goto bad;
-    }
-
-  /* Protocols that do not require connections could be optionally left
-   * unconnected.  That would allow servers to reply from a port other than
-   * the NFS_PORT.
-   */
-
-  error = psock_connect(&rpc->rc_so, saddr, sizeof(*saddr));
-  if (error < 0)
-    {
-      ferr("ERROR: psock_connect to PMAP port failed: %d", error);
-      goto bad;
-    }
+  prot = rpc->rc_sotype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
 
   /* Do the RPC to get a dynamic bounding with the server using ppmap.
    * Get port number for MOUNTD.
@@ -450,7 +524,7 @@ int rpcclnt_connect(FAR struct rpcclnt *rpc)
 
   request.sdata.pmap.prog = txdr_unsigned(RPCPROG_MNT);
   request.sdata.pmap.vers = txdr_unsigned(RPCMNT_VER3);
-  request.sdata.pmap.prot = txdr_unsigned(IPPROTO_UDP);
+  request.sdata.pmap.prot = txdr_unsigned(prot);
   request.sdata.pmap.port = 0;
 
   error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
@@ -462,20 +536,17 @@ int rpcclnt_connect(FAR struct rpcclnt *rpc)
       goto bad;
     }
 
-  sa = (FAR struct sockaddr_in *)saddr;
-  sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));
-
-  error = psock_connect(&rpc->rc_so, saddr, sizeof(*saddr));
+  error = rpcclnt_socket(rpc, fxdr_unsigned(uint32_t, response.rdata.pmap.port));
   if (error < 0)
     {
-      ferr("ERROR: psock_connect MOUNTD port failed: %d\n", error);
+      ferr("ERROR: rpcclnt_socket MOUNTD port failed: %d\n", error);
       goto bad;
     }
 
   /* Do RPC to mountd. */
 
   strncpy(request.mountd.mount.rpath, rpc->rc_path, 90);
-  request.mountd.mount.len =  txdr_unsigned(sizeof(request.mountd.mount.rpath));
+  request.mountd.mount.len = txdr_unsigned(sizeof(request.mountd.mount.rpath));
 
   error = rpcclnt_request(rpc, RPCMNT_MOUNT, RPCPROG_MNT, RPCMNT_VER3,
                           (FAR void *)&request.mountd,
@@ -488,7 +559,7 @@ int rpcclnt_connect(FAR struct rpcclnt *rpc)
       goto bad;
     }
 
-  error = fxdr_unsigned(uint32_t, response.mdata.mount.status);
+  error = -fxdr_unsigned(uint32_t, response.mdata.mount.status);
   if (error != 0)
     {
       ferr("ERROR: Bad mount status: %d\n", error);
@@ -502,18 +573,16 @@ int rpcclnt_connect(FAR struct rpcclnt *rpc)
    * NFS port in the socket.
    */
 
-  sa->sin_port = htons(PMAPPORT);
-
-  error = psock_connect(&rpc->rc_so, saddr, sizeof(*saddr));
+  error = rpcclnt_socket(rpc, 0);
   if (error < 0)
     {
-      ferr("ERROR: psock_connect PMAP port failed: %d\n", error);
+      ferr("ERROR: rpcclnt_socket PMAP port failed: %d\n", error);
       goto bad;
     }
 
   request.sdata.pmap.prog = txdr_unsigned(NFS_PROG);
   request.sdata.pmap.vers = txdr_unsigned(NFS_VER3);
-  request.sdata.pmap.prot = txdr_unsigned(IPPROTO_UDP);
+  request.sdata.pmap.prot = txdr_unsigned(prot);
   request.sdata.pmap.port = 0;
 
   error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
@@ -527,19 +596,17 @@ int rpcclnt_connect(FAR struct rpcclnt *rpc)
       goto bad;
     }
 
-  sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));
-
-  error = psock_connect(&rpc->rc_so, saddr, sizeof(*saddr));
+  error = rpcclnt_socket(rpc, fxdr_unsigned(uint32_t, response.rdata.pmap.port));
   if (error < 0)
     {
-      ferr("ERROR: psock_connect NFS port returns %d\n", error);
+      ferr("ERROR: rpcclnt_socket NFS port returns %d\n", error);
       goto bad;
     }
 
   return OK;
 
 bad:
-  rpcclnt_disconnect(rpc);
+  psock_close(&rpc->rc_so);
   return error;
 }
 
@@ -553,22 +620,6 @@ bad:
 
 void rpcclnt_disconnect(FAR struct rpcclnt *rpc)
 {
-  psock_close(&rpc->rc_so);
-}
-
-/****************************************************************************
- * Name: rpcclnt_umount
- *
- * Description:
- *   Un-mount the NFS file system.
- *
- ****************************************************************************/
-
-int rpcclnt_umount(FAR struct rpcclnt *rpc)
-{
-  FAR struct sockaddr *saddr;
-  FAR struct sockaddr_in *sa;
-
   union
   {
     struct rpc_call_pmap   sdata;
@@ -582,27 +633,20 @@ int rpcclnt_umount(FAR struct rpcclnt *rpc)
   } response;
 
   int error;
+  int prot;
 
-  saddr = rpc->rc_name;
-  sa = (FAR struct sockaddr_in *)saddr;
-
-  /* Do the RPC to get a dynamic bounding with the server using ppmap.
-   * Get port number for MOUNTD.
-   */
-
-  sa->sin_port = htons(PMAPPORT);
-
-  error = psock_connect(&rpc->rc_so, saddr, sizeof(*saddr));
+  error = rpcclnt_socket(rpc, 0);
   if (error < 0)
     {
-      ferr("ERROR: psock_connect failed [port=%d]: %d\n",
-            ntohs(sa->sin_port), error);
+      ferr("ERROR: rpcclnt_socket failed: %d\n", error);
       goto bad;
     }
 
+  prot = rpc->rc_sotype == SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP;
+
   request.sdata.pmap.prog = txdr_unsigned(RPCPROG_MNT);
   request.sdata.pmap.vers = txdr_unsigned(RPCMNT_VER3);
-  request.sdata.pmap.prot = txdr_unsigned(IPPROTO_UDP);
+  request.sdata.pmap.prot = txdr_unsigned(prot);
   request.sdata.pmap.port = 0;
 
   error = rpcclnt_request(rpc, PMAPPROC_GETPORT, PMAPPROG, PMAPVERS,
@@ -616,20 +660,17 @@ int rpcclnt_umount(FAR struct rpcclnt *rpc)
       goto bad;
     }
 
-  sa->sin_port = htons(fxdr_unsigned(uint32_t, response.rdata.pmap.port));
-
-  error = psock_connect(&rpc->rc_so, saddr, sizeof(*saddr));
+  error = rpcclnt_socket(rpc, fxdr_unsigned(uint32_t, response.rdata.pmap.port));
   if (error < 0)
     {
-      ferr("ERROR: psock_connect failed [port=%d]: %d\n",
-            ntohs(sa->sin_port), error);
+      ferr("ERROR: rpcclnt_socket failed: %d\n", error);
       goto bad;
     }
 
   /* Do RPC to umountd. */
 
-  strncpy(request.mountd.umount.rpath, rpc->rc_path, 92);
-  request.mountd.umount.len =  txdr_unsigned(sizeof(request.mountd.umount.rpath));
+  strncpy(request.mountd.umount.rpath, rpc->rc_path, 90);
+  request.mountd.umount.len = txdr_unsigned(sizeof(request.mountd.umount.rpath));
 
   error = rpcclnt_request(rpc, RPCMNT_UMOUNT, RPCPROG_MNT, RPCMNT_VER3,
                           (FAR void *)&request.mountd,
@@ -642,11 +683,8 @@ int rpcclnt_umount(FAR struct rpcclnt *rpc)
       goto bad;
     }
 
-  return OK;
-
 bad:
-  rpcclnt_disconnect(rpc);
-  return error;
+  psock_close(&rpc->rc_so);
 }
 
 /****************************************************************************
@@ -676,7 +714,7 @@ int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
 
   /* Get a new (non-zero) xid */
 
-  xid = rpcclnt_newxid();
+  xid = ++rpc->rc_xid;
 
   /* Initialize the RPC header fields */
 
@@ -712,7 +750,7 @@ int rpcclnt_request(FAR struct rpcclnt *rpc, int procnum, int prog,
 
       else
         {
-          error = rpcclnt_reply(rpc, response, resplen);
+          error = rpcclnt_reply(rpc, xid, response, resplen);
           if (error != OK)
             {
               finfo("ERROR rpcclnt_reply failed: %d\n", error);
diff --git a/include/nuttx/net/netconfig.h b/include/nuttx/net/netconfig.h
index 7ed1dbd..ae5a9c2 100644
--- a/include/nuttx/net/netconfig.h
+++ b/include/nuttx/net/netconfig.h
@@ -54,6 +54,7 @@
 
 #include <stdint.h>
 #include <nuttx/config.h>
+#include <nuttx/net/ethernet.h>
 
 /****************************************************************************
  * Public Definitions