You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/09/24 15:09:38 UTC

[incubator-nuttx] 04/04: net/local: add socket message control support

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

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

commit 1b5d6aa068ec5113ae5b105f8120b865ea89b5a0
Author: chao.an <an...@xiaomi.com>
AuthorDate: Wed Sep 15 23:22:18 2021 +0800

    net/local: add socket message control support
    
    Signed-off-by: chao.an <an...@xiaomi.com>
---
 net/local/Kconfig         |   6 ++
 net/local/local.h         |  26 ++++++---
 net/local/local_accept.c  |   3 +
 net/local/local_conn.c    |  26 +++++++++
 net/local/local_connect.c |   3 +
 net/local/local_recvmsg.c | 111 ++++++++++++++++++++++++++++++++++--
 net/local/local_sendmsg.c | 139 ++++++++++++++++++++++++++++++++++++++++++++--
 7 files changed, 296 insertions(+), 18 deletions(-)

diff --git a/net/local/Kconfig b/net/local/Kconfig
index e32c0b3..158282b 100644
--- a/net/local/Kconfig
+++ b/net/local/Kconfig
@@ -33,6 +33,12 @@ config NET_LOCAL_DGRAM
 	---help---
 		Enable support for Unix domain SOCK_DGRAM type sockets
 
+config NET_LOCAL_SCM
+	bool "Unix domain socket control message"
+	default n
+	---help---
+		Enable support for Unix domain socket control message
+
 endif # NET_LOCAL
 
 endmenu # Unix Domain Sockets
diff --git a/net/local/local.h b/net/local/local.h
index c746b51..df0777d 100644
--- a/net/local/local.h
+++ b/net/local/local.h
@@ -46,6 +46,7 @@
  ****************************************************************************/
 
 #define LOCAL_NPOLLWAITERS 2
+#define LOCAL_NCONTROLFDS  4
 
 /* Packet format in FIFO:
  *
@@ -136,15 +137,22 @@ struct local_conn_s
 
   /* Fields common to SOCK_STREAM and SOCK_DGRAM */
 
-  uint8_t lc_crefs;            /* Reference counts on this instance */
-  uint8_t lc_proto;            /* SOCK_STREAM or SOCK_DGRAM */
-  uint8_t lc_type;             /* See enum local_type_e */
-  uint8_t lc_state;            /* See enum local_state_e */
-  struct file lc_infile;       /* File for read-only FIFO (peers) */
-  struct file lc_outfile;      /* File descriptor of write-only FIFO (peers) */
-  char lc_path[UNIX_PATH_MAX]; /* Path assigned by bind() */
-  int32_t lc_instance_id;      /* Connection instance ID for stream
-                                * server<->client connection pair */
+  uint8_t lc_crefs;              /* Reference counts on this instance */
+  uint8_t lc_proto;              /* SOCK_STREAM or SOCK_DGRAM */
+  uint8_t lc_type;               /* See enum local_type_e */
+  uint8_t lc_state;              /* See enum local_state_e */
+  struct file lc_infile;         /* File for read-only FIFO (peers) */
+  struct file lc_outfile;        /* File descriptor of write-only FIFO (peers) */
+  char lc_path[UNIX_PATH_MAX];   /* Path assigned by bind() */
+  int32_t lc_instance_id;        /* Connection instance ID for stream
+                                  * server<->client connection pair */
+#ifdef CONFIG_NET_LOCAL_SCM
+  FAR struct local_conn_s *
+                        lc_peer; /* Peer connection instance */
+  uint16_t lc_cfpcount;          /* Control file pointer counter */
+  FAR struct file *
+     lc_cfps[LOCAL_NCONTROLFDS]; /* Socket message control filep */
+#endif /* CONFIG_NET_LOCAL_SCM */
 
 #ifdef CONFIG_NET_LOCAL_STREAM
   /* SOCK_STREAM fields common to both client and server */
diff --git a/net/local/local_accept.c b/net/local/local_accept.c
index 4529dab..8d4bec5 100644
--- a/net/local/local_accept.c
+++ b/net/local/local_accept.c
@@ -168,6 +168,9 @@ int local_accept(FAR struct socket *psock, FAR struct sockaddr *addr,
               conn->lc_type   = LOCAL_TYPE_PATHNAME;
               conn->lc_state  = LOCAL_STATE_CONNECTED;
               conn->lc_psock  = psock;
+#ifdef CONFIG_NET_LOCAL_SCM
+              conn->lc_peer   = client;
+#endif /* CONFIG_NET_LOCAL_SCM */
 
               strncpy(conn->lc_path, client->lc_path, UNIX_PATH_MAX - 1);
               conn->lc_path[UNIX_PATH_MAX - 1] = '\0';
diff --git a/net/local/local_conn.c b/net/local/local_conn.c
index 3b27400..c968cca 100644
--- a/net/local/local_conn.c
+++ b/net/local/local_conn.c
@@ -161,12 +161,24 @@ FAR struct local_conn_s *local_alloc(void)
 
 void local_free(FAR struct local_conn_s *conn)
 {
+#ifdef CONFIG_NET_LOCAL_SCM
+  int i;
+#endif /* CONFIG_NET_LOCAL_SCM */
+
   DEBUGASSERT(conn != NULL);
 
   /* Remove the server from the list of listeners. */
 
   net_lock();
   dq_rem(&conn->lc_node, &g_local_connections);
+
+#ifdef CONFIG_NET_LOCAL_SCM
+  if (local_peerconn(conn) && conn->lc_peer)
+    {
+      conn->lc_peer->lc_peer = NULL;
+    }
+#endif /* CONFIG_NET_LOCAL_SCM */
+
   net_unlock();
 
   /* Make sure that the read-only FIFO is closed */
@@ -185,6 +197,20 @@ void local_free(FAR struct local_conn_s *conn)
       conn->lc_outfile.f_inode = NULL;
     }
 
+#ifdef CONFIG_NET_LOCAL_SCM
+  /* Free the pending control file pointer */
+
+  for (i = 0; i < conn->lc_cfpcount; i++)
+    {
+      if (conn->lc_cfps[i])
+        {
+          file_close(conn->lc_cfps[i]);
+          kmm_free(conn->lc_cfps[i]);
+          conn->lc_cfps[i] = NULL;
+        }
+    }
+#endif /* CONFIG_NET_LOCAL_SCM */
+
 #ifdef CONFIG_NET_LOCAL_STREAM
   /* Destroy all FIFOs associted with the connection */
 
diff --git a/net/local/local_connect.c b/net/local/local_connect.c
index b3aff31..1c3f407 100644
--- a/net/local/local_connect.c
+++ b/net/local/local_connect.c
@@ -301,6 +301,9 @@ int psock_local_connect(FAR struct socket *psock,
                         UNIX_PATH_MAX - 1);
                 client->lc_path[UNIX_PATH_MAX - 1] = '\0';
                 client->lc_instance_id = local_generate_instance_id();
+#ifdef CONFIG_NET_LOCAL_SCM
+                client->lc_peer = conn;
+#endif /* CONFIG_NET_LOCAL_SCM */
 
                 /* The client is now bound to an address */
 
diff --git a/net/local/local_recvmsg.c b/net/local/local_recvmsg.c
index 6cc1237..9ef2287 100644
--- a/net/local/local_recvmsg.c
+++ b/net/local/local_recvmsg.c
@@ -33,6 +33,8 @@
 #include <assert.h>
 #include <debug.h>
 
+#include <nuttx/kmalloc.h>
+#include <nuttx/fs/fs.h>
 #include <nuttx/net/net.h>
 
 #include "socket/socket.h"
@@ -106,6 +108,93 @@ static int psock_fifo_read(FAR struct socket *psock, FAR void *buf,
 }
 
 /****************************************************************************
+ * Name: local_recvctl
+ *
+ * Description:
+ *   Handle the socket message conntrol field
+ *
+ * Input Parameters:
+ *   conn     Local connection instance
+ *   msg      Message to send
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_LOCAL_SCM
+static void local_recvctl(FAR struct local_conn_s *conn,
+                          FAR struct msghdr *msg)
+{
+  FAR struct local_conn_s *peer;
+  struct cmsghdr *cmsg;
+  int count;
+  int *fds;
+  int i;
+
+  net_lock();
+
+  cmsg  = CMSG_FIRSTHDR(msg);
+  count = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
+  cmsg->cmsg_len = 0;
+
+  if (count == 0)
+    {
+      goto out;
+    }
+
+  if (conn->lc_peer == NULL)
+    {
+      peer = local_peerconn(conn);
+      if (peer == NULL)
+        {
+          goto out;
+        }
+    }
+  else
+    {
+      peer = conn;
+    }
+
+  if (peer->lc_cfpcount == 0)
+    {
+      goto out;
+    }
+
+  fds = (int *)CMSG_DATA(cmsg);
+
+  count = count > peer->lc_cfpcount ?
+                  peer->lc_cfpcount : count;
+  for (i = 0; i < count; i++)
+    {
+      fds[i] = file_dup(peer->lc_cfps[i], 0);
+      file_close(peer->lc_cfps[i]);
+      kmm_free(peer->lc_cfps[i]);
+      peer->lc_cfps[i] = NULL;
+      peer->lc_cfpcount--;
+      if (fds[i] < 0)
+        {
+          i++;
+          break;
+        }
+    }
+
+  if (i > 0)
+    {
+      if (peer->lc_cfpcount)
+        {
+          memmove(peer->lc_cfps[0], peer->lc_cfps[i],
+                  sizeof(FAR void *) * peer->lc_cfpcount);
+        }
+
+      cmsg->cmsg_len   = CMSG_LEN(sizeof(int) * i);
+      cmsg->cmsg_level = SOL_SOCKET;
+      cmsg->cmsg_type  = SCM_RIGHTS;
+    }
+
+out:
+  net_unlock();
+}
+#endif /* CONFIG_NET_LOCAL_SCM */
+
+/****************************************************************************
  * Name: psock_stream_recvfrom
  *
  * Description:
@@ -382,10 +471,10 @@ errout_with_halfduplex:
 ssize_t local_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
                       int flags)
 {
+  FAR socklen_t *fromlen = &msg->msg_namelen;
+  FAR struct sockaddr *from = msg->msg_name;
   FAR void *buf = msg->msg_iov->iov_base;
   size_t len = msg->msg_iov->iov_len;
-  FAR struct sockaddr *from = msg->msg_name;
-  FAR socklen_t *fromlen = &msg->msg_namelen;
 
   DEBUGASSERT(psock && psock->s_conn && buf);
 
@@ -394,7 +483,7 @@ ssize_t local_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
 #ifdef CONFIG_NET_LOCAL_STREAM
   if (psock->s_type == SOCK_STREAM)
     {
-      return psock_stream_recvfrom(psock, buf, len, flags, from, fromlen);
+      len = psock_stream_recvfrom(psock, buf, len, flags, from, fromlen);
     }
   else
 #endif
@@ -402,15 +491,27 @@ ssize_t local_recvmsg(FAR struct socket *psock, FAR struct msghdr *msg,
 #ifdef CONFIG_NET_LOCAL_DGRAM
   if (psock->s_type == SOCK_DGRAM)
     {
-      return psock_dgram_recvfrom(psock, buf, len, flags, from, fromlen);
+      len = psock_dgram_recvfrom(psock, buf, len, flags, from, fromlen);
     }
   else
 #endif
     {
       DEBUGPANIC();
       nerr("ERROR: Unrecognized socket type: %" PRIu8 "\n", psock->s_type);
-      return -EINVAL;
+      len = -EINVAL;
+    }
+
+#ifdef CONFIG_NET_LOCAL_SCM
+  /* Receive the control message */
+
+  if (len >= 0 && msg->msg_control &&
+      msg->msg_controllen > sizeof(struct cmsghdr))
+    {
+      local_recvctl(psock->s_conn, msg);
     }
+#endif /* CONFIG_NET_LOCAL_SCM */
+
+  return len;
 }
 
 #endif /* CONFIG_NET && CONFIG_NET_LOCAL */
diff --git a/net/local/local_sendmsg.c b/net/local/local_sendmsg.c
index dd50f40..31c2114 100644
--- a/net/local/local_sendmsg.c
+++ b/net/local/local_sendmsg.c
@@ -32,6 +32,7 @@
 #include <assert.h>
 #include <debug.h>
 
+#include <nuttx/kmalloc.h>
 #include <nuttx/net/net.h>
 
 #include "socket/socket.h"
@@ -42,6 +43,105 @@
  ****************************************************************************/
 
 /****************************************************************************
+ * Name: local_sendctl
+ *
+ * Description:
+ *   Handle the socket message conntrol field
+ *
+ * Input Parameters:
+ *   conn     Local connection instance
+ *   msg      Message to send
+ *
+ * Returned Value:
+ *  On any failure, a negated errno value is returned
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_LOCAL_SCM
+static int local_sendctl(FAR struct local_conn_s *conn,
+                         FAR struct msghdr *msg)
+{
+  FAR struct local_conn_s *peer;
+  FAR struct file *filep2;
+  FAR struct file *filep;
+  struct cmsghdr *cmsg;
+  int count;
+  int *fds;
+  int ret;
+  int i;
+
+  net_lock();
+
+  peer = conn->lc_peer;
+  if (peer == NULL)
+    {
+      peer = conn;
+    }
+
+  for_each_cmsghdr(cmsg, msg)
+    {
+      if (!CMSG_OK(msg, cmsg) ||
+          cmsg->cmsg_level != SOL_SOCKET ||
+          cmsg->cmsg_type != SCM_RIGHTS)
+        {
+          ret = -EOPNOTSUPP;
+          goto fail;
+        }
+
+      fds = (int *)CMSG_DATA(cmsg);
+      count = (cmsg->cmsg_len - sizeof(struct cmsghdr)) / sizeof(int);
+
+      if (count + peer->lc_cfpcount > LOCAL_NCONTROLFDS)
+        {
+          ret = -EMFILE;
+          goto fail;
+        }
+
+      for (i = 0; i < count; i++)
+        {
+          ret = fs_getfilep(fds[i], &filep);
+          if (ret < 0)
+            {
+              goto fail;
+            }
+
+          filep2 = (FAR struct file *)kmm_zalloc(sizeof(*filep2));
+          if (!filep2)
+            {
+              ret = -ENOMEM;
+              goto fail;
+            }
+
+          ret = file_dup2(filep, filep2);
+          if (ret < 0)
+            {
+              kmm_free(filep2);
+              goto fail;
+            }
+
+          peer->lc_cfps[peer->lc_cfpcount++] = filep2;
+        }
+    }
+
+  net_unlock();
+
+  return count;
+
+fail:
+  while (i-- > 0)
+    {
+      file_close(peer->lc_cfps[--peer->lc_cfpcount]);
+      kmm_free(peer->lc_cfps[peer->lc_cfpcount]);
+      peer->lc_cfps[peer->lc_cfpcount] = NULL;
+    }
+
+  net_unlock();
+
+  return ret;
+}
+#endif /* CONFIG_NET_LOCAL_SCM */
+
+/****************************************************************************
  * Name: local_send
  *
  * Description:
@@ -284,13 +384,44 @@ errout_with_halfduplex:
 ssize_t local_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
                       int flags)
 {
-  FAR const struct iovec *buf = msg->msg_iov;
-  size_t len = msg->msg_iovlen;
   FAR const struct sockaddr *to = msg->msg_name;
+  FAR const struct iovec *buf = msg->msg_iov;
   socklen_t tolen = msg->msg_namelen;
+  size_t len = msg->msg_iovlen;
+#ifdef CONFIG_NET_LOCAL_SCM
+  FAR struct local_conn_s *conn = psock->s_conn;
+  int count;
+
+  if (msg->msg_control &&
+      msg->msg_controllen > sizeof(struct cmsghdr))
+    {
+      count = local_sendctl(conn, msg);
+      if (count < 0)
+        {
+          return count;
+        }
+    }
+#endif /* CONFIG_NET_LOCAL_SCM */
+
+  len = to ? local_sendto(psock, buf, len, flags, to, tolen) :
+             local_send(psock, buf, len, flags);
+#ifdef CONFIG_NET_LOCAL_SCM
+  if (len < 0 && count > 0)
+    {
+      net_lock();
+
+      while (count-- > 0)
+        {
+          file_close(conn->lc_cfps[--conn->lc_cfpcount]);
+          kmm_free(conn->lc_cfps[conn->lc_cfpcount]);
+          conn->lc_cfps[conn->lc_cfpcount] = NULL;
+        }
+
+      net_unlock();
+    }
+#endif
 
-  return to ? local_sendto(psock, buf, len, flags, to, tolen) :
-              local_send(psock, buf, len, flags);
+  return len;
 }
 
 #endif /* CONFIG_NET && CONFIG_NET_LOCAL */