perfmon: topdown events as peusdo events
[vpp.git] / src / vppinfra / socket.c
index 87a9333..2921e00 100644 (file)
@@ -38,6 +38,7 @@
 #include <stdio.h>
 #include <string.h>            /* strchr */
 #define __USE_GNU
+#define _GNU_SOURCE
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <vppinfra/mem.h>
 #include <vppinfra/vec.h>
 #include <vppinfra/socket.h>
+#include <vppinfra/linux/netns.h>
 #include <vppinfra/format.h>
 #include <vppinfra/error.h>
 
-void
+#ifndef __GLIBC__
+/* IPPORT_USERRESERVED is not part of musl libc. */
+#define IPPORT_USERRESERVED 5000
+#endif
+
+__clib_export void
 clib_socket_tx_add_formatted (clib_socket_t * s, char *fmt, ...)
 {
   va_list va;
@@ -73,7 +80,7 @@ find_free_port (word sock)
     {
       struct sockaddr_in a;
 
-      memset (&a, 0, sizeof (a));      /* Warnings be gone */
+      clib_memset (&a, 0, sizeof (a)); /* Warnings be gone */
 
       a.sin_family = PF_INET;
       a.sin_addr.s_addr = INADDR_ANY;
@@ -107,6 +114,18 @@ socket_config (char *config,
       *addr_len = sizeof (su[0]);
     }
 
+  /* Treat everything that starts with @ as an abstract socket. */
+  else if (config[0] == '@')
+    {
+      struct sockaddr_un *su = addr;
+      su->sun_family = PF_LOCAL;
+      clib_memcpy (&su->sun_path, config,
+                  clib_min (sizeof (su->sun_path), 1 + strlen (config)));
+
+      *addr_len = sizeof (su->sun_family) + strlen (config);
+      su->sun_path[0] = '\0';
+    }
+
   /* Hostname or hostname:port or port. */
   else
     {
@@ -278,7 +297,7 @@ default_socket_sendmsg (clib_socket_t * s, void *msg, int msglen,
 {
   struct msghdr mh = { 0 };
   struct iovec iov[1];
-  char ctl[CMSG_SPACE (sizeof (int)) * num_fds];
+  char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
   int rv;
 
   iov[0].iov_base = msg;
@@ -289,7 +308,7 @@ default_socket_sendmsg (clib_socket_t * s, void *msg, int msglen,
   if (num_fds > 0)
     {
       struct cmsghdr *cmsg;
-      memset (&ctl, 0, sizeof (ctl));
+      clib_memset (&ctl, 0, sizeof (ctl));
       mh.msg_control = ctl;
       mh.msg_controllen = sizeof (ctl);
       cmsg = CMSG_FIRSTHDR (&mh);
@@ -309,12 +328,16 @@ static clib_error_t *
 default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
                        int fds[], int num_fds)
 {
+#ifdef __linux__
   char ctl[CMSG_SPACE (sizeof (int) * num_fds) +
           CMSG_SPACE (sizeof (struct ucred))];
+  struct ucred *cr = 0;
+#else
+  char ctl[CMSG_SPACE (sizeof (int) * num_fds)];
+#endif
   struct msghdr mh = { 0 };
   struct iovec iov[1];
   ssize_t size;
-  struct ucred *cr = 0;
   struct cmsghdr *cmsg;
 
   iov[0].iov_base = msg;
@@ -324,7 +347,7 @@ default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
   mh.msg_control = ctl;
   mh.msg_controllen = sizeof (ctl);
 
-  memset (ctl, 0, sizeof (ctl));
+  clib_memset (ctl, 0, sizeof (ctl));
 
   /* receive the incoming message */
   size = recvmsg (s->fd, &mh, 0);
@@ -340,6 +363,7 @@ default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
     {
       if (cmsg->cmsg_level == SOL_SOCKET)
        {
+#ifdef __linux__
          if (cmsg->cmsg_type == SCM_CREDENTIALS)
            {
              cr = (struct ucred *) CMSG_DATA (cmsg);
@@ -347,9 +371,12 @@ default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
              s->gid = cr->gid;
              s->pid = cr->pid;
            }
-         else if (cmsg->cmsg_type == SCM_RIGHTS)
+         else
+#endif
+         if (cmsg->cmsg_type == SCM_RIGHTS)
            {
-             clib_memcpy (fds, CMSG_DATA (cmsg), num_fds * sizeof (int));
+             clib_memcpy_fast (fds, CMSG_DATA (cmsg),
+                               num_fds * sizeof (int));
            }
        }
       cmsg = CMSG_NXTHDR (&mh, cmsg);
@@ -372,7 +399,7 @@ socket_init_funcs (clib_socket_t * s)
     s->recvmsg_func = default_socket_recvmsg;
 }
 
-clib_error_t *
+__clib_export clib_error_t *
 clib_socket_init (clib_socket_t * s)
 {
   union
@@ -381,7 +408,7 @@ clib_socket_init (clib_socket_t * s)
     struct sockaddr_un su;
   } addr;
   socklen_t addr_len = 0;
-  int socket_type;
+  int socket_type, rv;
   clib_error_t *error = 0;
   word port;
 
@@ -426,7 +453,8 @@ clib_socket_init (clib_socket_t * s)
              need_bind = 0;
            }
        }
-      if (addr.sa.sa_family == PF_LOCAL)
+      if (addr.sa.sa_family == PF_LOCAL &&
+         ((struct sockaddr_un *) &addr)->sun_path[0] != 0)
        unlink (((struct sockaddr_un *) &addr)->sun_path);
 
       /* Make address available for multiple users. */
@@ -436,6 +464,7 @@ clib_socket_init (clib_socket_t * s)
          clib_unix_warning ("setsockopt SO_REUSEADDR fails");
       }
 
+#if __linux__
       if (addr.sa.sa_family == PF_LOCAL && s->flags & CLIB_SOCKET_F_PASSCRED)
        {
          int x = 1;
@@ -447,6 +476,7 @@ clib_socket_init (clib_socket_t * s)
              goto done;
            }
        }
+#endif
 
       if (need_bind && bind (s->fd, &addr.sa, addr_len) < 0)
        {
@@ -461,8 +491,9 @@ clib_socket_init (clib_socket_t * s)
                                          s->fd, s->config);
          goto done;
        }
-      if (addr.sa.sa_family == PF_LOCAL
-         && s->flags & CLIB_SOCKET_F_ALLOW_GROUP_WRITE)
+      if (addr.sa.sa_family == PF_LOCAL &&
+         s->flags & CLIB_SOCKET_F_ALLOW_GROUP_WRITE &&
+         ((struct sockaddr_un *) &addr)->sun_path[0] != 0)
        {
          struct stat st = { 0 };
          if (stat (((struct sockaddr_un *) &addr)->sun_path, &st) < 0)
@@ -492,14 +523,26 @@ clib_socket_init (clib_socket_t * s)
          goto done;
        }
 
-      if (connect (s->fd, &addr.sa, addr_len) < 0
-         && !((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
-              errno == EINPROGRESS))
+      while ((rv = connect (s->fd, &addr.sa, addr_len)) < 0
+            && errno == EAGAIN)
+       ;
+      if (rv < 0 && !((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
+                     errno == EINPROGRESS))
        {
          error = clib_error_return_unix (0, "connect (fd %d, '%s')",
                                          s->fd, s->config);
          goto done;
        }
+      /* Connect was blocking so set fd to non-blocking now unless
+       * blocking mode explicitly requested. */
+      if (!(s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
+         !(s->flags & CLIB_SOCKET_F_BLOCKING) &&
+         fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
+       {
+         error = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
+                                         s->fd, s->config);
+         goto done;
+       }
     }
 
   return error;
@@ -510,13 +553,49 @@ done:
   return error;
 }
 
-clib_error_t *
+__clib_export clib_error_t *
+clib_socket_init_netns (clib_socket_t *s, u8 *namespace)
+{
+  if (namespace == NULL || namespace[0] == 0)
+    return clib_socket_init (s);
+
+  clib_error_t *error;
+  int old_netns_fd, nfd = -1;
+
+  old_netns_fd = clib_netns_open (NULL /* self */);
+  if ((nfd = clib_netns_open (namespace)) == -1)
+    {
+      error = clib_error_return_unix (0, "clib_netns_open '%s'", namespace);
+      goto done;
+    }
+
+  if (clib_setns (nfd) == -1)
+    {
+      error = clib_error_return_unix (0, "setns '%s'", namespace);
+      goto done;
+    }
+
+  error = clib_socket_init (s);
+
+done:
+  if (clib_setns (old_netns_fd) == -1)
+    clib_warning ("Cannot set old ns");
+
+  close (old_netns_fd);
+
+  if (-1 != nfd)
+    close (nfd);
+
+  return error;
+}
+
+__clib_export clib_error_t *
 clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
 {
   clib_error_t *err = 0;
   socklen_t len = 0;
 
-  memset (client, 0, sizeof (client[0]));
+  clib_memset (client, 0, sizeof (client[0]));
 
   /* Accept the new socket connection. */
   client->fd = accept (server->fd, 0, 0);