vppinfra: refactor clib_socket_init, add linux netns support 78/38078/4
authorDamjan Marion <dmarion@me.com>
Mon, 30 Jan 2023 10:48:38 +0000 (11:48 +0100)
committerOle Tr�an <otroan@employees.org>
Mon, 6 Feb 2023 10:17:40 +0000 (10:17 +0000)
Type: improvement
Change-Id: Ida2d044bccf0bc8914b4fe7d383f827400fa6a52
Signed-off-by: Damjan Marion <dmarion@me.com>
src/plugins/memif/cli.c
src/plugins/memif/memif.c
src/plugins/memif/memif_api.c
src/plugins/memif/private.h
src/plugins/snort/main.c
src/vppinfra/clib.h
src/vppinfra/socket.c
src/vppinfra/socket.h

index 9a0ded8..3d3a681 100644 (file)
@@ -73,7 +73,7 @@ memif_socket_filename_create_command_fn (vlib_main_t * vm,
       return clib_error_return (0, "Invalid socket filename");
     }
 
-  err = memif_socket_filename_add_del (1, socket_id, socket_filename);
+  err = memif_socket_filename_add_del (1, socket_id, (char *) socket_filename);
 
   vec_free (socket_filename);
 
index 12d81ee..eee38f0 100644 (file)
@@ -641,8 +641,8 @@ memif_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
            {
               clib_memset (sock, 0, sizeof(clib_socket_t));
              sock->config = (char *) msf->filename;
-             sock->flags = CLIB_SOCKET_F_IS_CLIENT | CLIB_SOCKET_F_SEQPACKET |
-                           CLIB_SOCKET_F_BLOCKING;
+             sock->is_seqpacket = 1;
+             sock->is_blocking = 1;
 
              if ((err = clib_socket_init (sock)))
                {
@@ -675,79 +675,22 @@ memif_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
   return 0;
 }
 
-/* *INDENT-OFF* */
 VLIB_REGISTER_NODE (memif_process_node,static) = {
   .function = memif_process,
   .type = VLIB_NODE_TYPE_PROCESS,
   .name = "memif-process",
 };
-/* *INDENT-ON* */
-
-static clib_error_t *
-memif_add_socket_file (u32 sock_id, u8 *socket_filename)
-{
-  memif_main_t *mm = &memif_main;
-  uword *p;
-  memif_socket_file_t *msf;
-
-  p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
-  if (p)
-    {
-      msf = pool_elt_at_index (mm->socket_files, *p);
-      if (strcmp ((char *) msf->filename, (char *) socket_filename) == 0)
-       {
-         /* Silently accept identical "add". */
-         return 0;
-       }
-
-      /* But don't allow a direct add of a different filename. */
-      return vnet_error (VNET_ERR_ENTRY_ALREADY_EXISTS,
-                        "entry already exists");
-    }
-
-  pool_get (mm->socket_files, msf);
-  clib_memset (msf, 0, sizeof (memif_socket_file_t));
-
-  msf->filename = socket_filename;
-  msf->socket_id = sock_id;
 
-  hash_set (mm->socket_file_index_by_sock_id, sock_id,
-           msf - mm->socket_files);
-
-  return 0;
-}
-
-static clib_error_t *
-memif_delete_socket_file (u32 sock_id)
+clib_error_t *
+memif_socket_filename_add_del (u8 is_add, u32 sock_id, char *sock_filename)
 {
   memif_main_t *mm = &memif_main;
   uword *p;
   memif_socket_file_t *msf;
-
-  p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
-  if (!p)
-    /* Don't delete non-existent entries. */
-    return vnet_error (VNET_ERR_INVALID_ARGUMENT,
-                      "socket file with id %u does not exist", sock_id);
-
-  msf = pool_elt_at_index (mm->socket_files, *p);
-  if (msf->ref_cnt > 0)
-    return vnet_error (VNET_ERR_UNEXPECTED_INTF_STATE,
-                      "socket file '%s' is in use", msf->filename);
-
-  vec_free (msf->filename);
-  pool_put (mm->socket_files, msf);
-
-  hash_unset (mm->socket_file_index_by_sock_id, sock_id);
-
-  return 0;
-}
-
-clib_error_t *
-memif_socket_filename_add_del (u8 is_add, u32 sock_id, u8 *sock_filename)
-{
+  clib_error_t *err = 0;
   char *dir = 0, *tmp;
   u32 idx = 0;
+  u8 *name = 0;
 
   /* allow adding socket id 0 */
   if (sock_id == 0 && is_add == 0)
@@ -758,70 +701,95 @@ memif_socket_filename_add_del (u8 is_add, u32 sock_id, u8 *sock_filename)
                       "socked id is not specified");
 
   if (is_add == 0)
-    return memif_delete_socket_file (sock_id);
+    {
+      p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
+      if (!p)
+       /* Don't delete non-existent entries. */
+       return vnet_error (VNET_ERR_INVALID_ARGUMENT,
+                          "socket file with id %u does not exist", sock_id);
+
+      msf = pool_elt_at_index (mm->socket_files, *p);
+      if (msf->ref_cnt > 0)
+       return vnet_error (VNET_ERR_UNEXPECTED_INTF_STATE,
+                          "socket file '%s' is in use", msf->filename);
+
+      vec_free (msf->filename);
+      pool_put (mm->socket_files, msf);
+
+      hash_unset (mm->socket_file_index_by_sock_id, sock_id);
+
+      return 0;
+    }
 
   if (sock_filename == 0 || sock_filename[0] == 0)
     return vnet_error (VNET_ERR_INVALID_ARGUMENT,
                       "socket filename not specified");
 
-  if (sock_filename[0] != '/')
+  if (clib_socket_prefix_is_valid (sock_filename))
+    {
+      name = format (0, "%s%c", sock_filename, 0);
+    }
+  else if (sock_filename[0] == '/')
+    {
+      name = format (0, "%s%c", sock_filename, 0);
+    }
+  else
     {
-      clib_error_t *error;
-
       /* copy runtime dir path */
       vec_add (dir, vlib_unix_get_runtime_dir (),
               strlen (vlib_unix_get_runtime_dir ()));
       vec_add1 (dir, '/');
 
       /* if sock_filename contains dirs, add them to path */
-      tmp = strrchr ((char *) sock_filename, '/');
+      tmp = strrchr (sock_filename, '/');
       if (tmp)
        {
-         idx = tmp - (char *) sock_filename;
+         idx = tmp - sock_filename;
          vec_add (dir, sock_filename, idx);
        }
 
       vec_add1 (dir, '\0');
       /* create socket dir */
-      error = vlib_unix_recursive_mkdir (dir);
-      if (error)
+      if ((err = vlib_unix_recursive_mkdir (dir)))
        {
-         clib_error_free (error);
-         return vnet_error (VNET_ERR_SYSCALL_ERROR_1,
-                            "unable to create socket dir");
+         clib_error_free (err);
+         err = vnet_error (VNET_ERR_SYSCALL_ERROR_1,
+                           "unable to create socket dir");
+         goto done;
        }
 
-      sock_filename = format (0, "%s/%s%c", vlib_unix_get_runtime_dir (),
-                             sock_filename, 0);
+      name =
+       format (0, "%s/%s%c", vlib_unix_get_runtime_dir (), sock_filename, 0);
     }
-  else
-    {
-      sock_filename = vec_dup (sock_filename);
 
-      /* check if directory exists */
-      tmp = strrchr ((char *) sock_filename, '/');
-      if (tmp)
+  p = hash_get (mm->socket_file_index_by_sock_id, sock_id);
+  if (p)
+    {
+      msf = pool_elt_at_index (mm->socket_files, *p);
+      if (strcmp ((char *) msf->filename, (char *) name) == 0)
        {
-         idx = tmp - (char *) sock_filename;
-         vec_add (dir, sock_filename, idx);
-         vec_add1 (dir, '\0');
+         /* Silently accept identical "add". */
+         goto done;
        }
 
-      /* check dir existance and access rights for effective user/group IDs */
-      if ((dir == NULL)
-         ||
-         (faccessat ( /* ignored */ -1, dir, F_OK | R_OK | W_OK, AT_EACCESS)
-          < 0))
-       {
-         vec_free (dir);
-         return vnet_error (
-           VNET_ERR_INVALID_ARGUMENT,
-           "directory doesn't exist or no access permissions");
-       }
+      /* But don't allow a direct add of a different filename. */
+      err = vnet_error (VNET_ERR_ENTRY_ALREADY_EXISTS, "entry already exists");
+      goto done;
     }
-  vec_free (dir);
 
-  return memif_add_socket_file (sock_id, sock_filename);
+  pool_get (mm->socket_files, msf);
+  clib_memset (msf, 0, sizeof (memif_socket_file_t));
+
+  msf->filename = name;
+  msf->socket_id = sock_id;
+  name = 0;
+
+  hash_set (mm->socket_file_index_by_sock_id, sock_id, msf - mm->socket_files);
+
+done:
+  vec_free (name);
+  vec_free (dir);
+  return err;
 }
 
 clib_error_t *
@@ -949,24 +917,6 @@ memif_create_if (vlib_main_t *vm, memif_create_if_args_t *args)
   /* Create new socket file */
   if (msf->ref_cnt == 0)
     {
-      struct stat file_stat;
-
-      /* If we are creating listener make sure file doesn't exist or if it
-       * exists thn delete it if it is old socket file */
-      if (args->is_master && (stat ((char *) msf->filename, &file_stat) == 0))
-       {
-         if (S_ISSOCK (file_stat.st_mode))
-           {
-             unlink ((char *) msf->filename);
-           }
-         else
-           {
-             err = vnet_error (VNET_ERR_VALUE_EXIST, "File exists for %s",
-                               msf->filename);
-             goto done;
-           }
-       }
-
       mhash_init (&msf->dev_instance_by_id, sizeof (uword),
                  sizeof (memif_interface_id_t));
       msf->dev_instance_by_fd = hash_create (0, sizeof (uword));
@@ -1068,9 +1018,11 @@ memif_create_if (vlib_main_t *vm, memif_create_if_args_t *args)
 
       clib_memset (s, 0, sizeof (clib_socket_t));
       s->config = (char *) msf->filename;
-      s->flags = CLIB_SOCKET_F_IS_SERVER |
-       CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
-       CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
+      s->local_only = 1;
+      s->is_server = 1;
+      s->allow_group_write = 1;
+      s->is_seqpacket = 1;
+      s->passcred = 1;
 
       if ((err = clib_socket_init (s)))
        {
@@ -1159,9 +1111,7 @@ memif_init (vlib_main_t * vm)
    * for socket-id 0 to MEMIF_DEFAULT_SOCKET_FILENAME in the
    * default run-time directory.
    */
-  memif_socket_filename_add_del (1, 0, (u8 *) MEMIF_DEFAULT_SOCKET_FILENAME);
-
-  return 0;
+  return memif_socket_filename_add_del (1, 0, MEMIF_DEFAULT_SOCKET_FILENAME);
 }
 
 VLIB_INIT_FUNCTION (memif_init);
index 04c2c8d..9a2f42a 100644 (file)
@@ -48,8 +48,6 @@ void
   memif_main_t *mm = &memif_main;
   u8 is_add;
   u32 socket_id;
-  u32 len;
-  u8 *socket_filename;
   vl_api_memif_socket_filename_add_del_reply_t *rmp;
   int rv;
 
@@ -65,19 +63,10 @@ void
     }
 
   /* socket filename */
-  socket_filename = 0;
   mp->socket_filename[ARRAY_LEN (mp->socket_filename) - 1] = 0;
-  len = strlen ((char *) mp->socket_filename);
-  if (mp->is_add)
-    {
-      vec_validate (socket_filename, len);
-      memcpy (socket_filename, mp->socket_filename, len);
-    }
-
-  rv = vnet_api_error (
-    memif_socket_filename_add_del (is_add, socket_id, socket_filename));
 
-  vec_free (socket_filename);
+  rv = vnet_api_error (memif_socket_filename_add_del (
+    is_add, socket_id, (char *) mp->socket_filename));
 
 reply:
   REPLY_MACRO (VL_API_MEMIF_SOCKET_FILENAME_ADD_DEL_REPLY);
index 559062e..ba77cc1 100644 (file)
@@ -322,7 +322,7 @@ typedef struct
 } memif_create_if_args_t;
 
 clib_error_t *memif_socket_filename_add_del (u8 is_add, u32 sock_id,
-                                            u8 *sock_filename);
+                                            char *sock_filename);
 clib_error_t *memif_create_if (vlib_main_t *vm, memif_create_if_args_t *args);
 clib_error_t *memif_delete_if (vlib_main_t *vm, memif_if_t *mif);
 clib_error_t *memif_plugin_api_hookup (vlib_main_t * vm);
index 39c13a8..2430fcd 100644 (file)
@@ -259,8 +259,10 @@ snort_listener_init (vlib_main_t *vm)
   s = clib_mem_alloc (sizeof (clib_socket_t));
   clib_memset (s, 0, sizeof (clib_socket_t));
   s->config = (char *) sm->socket_name;
-  s->flags = CLIB_SOCKET_F_IS_SERVER | CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
-            CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;
+  s->is_server = 1;
+  s->allow_group_write = 1;
+  s->is_seqpacket = 1;
+  s->passcred = 1;
 
   if ((err = clib_socket_init (s)))
     {
index 746cb51..8d1e412 100644 (file)
 #define CLIB_UNIX
 #endif
 
+#ifdef __linux__
+#define CLIB_LINUX 1
+#else
+#define CLIB_LINUX 0
+#endif
+
 #include <vppinfra/types.h>
 #include <vppinfra/atomics.h>
 
index a3024ae..e61c369 100644 (file)
@@ -93,108 +93,6 @@ find_free_port (word sock)
   return port < 1 << 16 ? port : -1;
 }
 
-/* Convert a config string to a struct sockaddr and length for use
-   with bind or connect. */
-static clib_error_t *
-socket_config (char *config,
-              void *addr, socklen_t * addr_len, u32 ip4_default_address)
-{
-  clib_error_t *error = 0;
-
-  if (!config)
-    config = "";
-
-  /* Anything that begins with a / is a local PF_LOCAL socket. */
-  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[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
-    {
-      char *host_name;
-      int port = -1;
-      struct sockaddr_in *sa = addr;
-
-      host_name = 0;
-      port = -1;
-      if (config[0] != 0)
-       {
-         unformat_input_t i;
-
-         unformat_init_string (&i, config, strlen (config));
-         if (unformat (&i, "%s:%d", &host_name, &port)
-             || unformat (&i, "%s:0x%x", &host_name, &port))
-           ;
-         else if (unformat (&i, "%s", &host_name))
-           ;
-         else
-           error = clib_error_return (0, "unknown input `%U'",
-                                      format_unformat_error, &i);
-         unformat_free (&i);
-
-         if (error)
-           goto done;
-       }
-
-      sa->sin_family = PF_INET;
-      *addr_len = sizeof (sa[0]);
-      if (port != -1)
-       sa->sin_port = htons (port);
-      else
-       sa->sin_port = 0;
-
-      if (host_name)
-       {
-         struct in_addr host_addr;
-
-         /* Recognize localhost to avoid host lookup in most common cast. */
-         if (!strcmp (host_name, "localhost"))
-           sa->sin_addr.s_addr = htonl (INADDR_LOOPBACK);
-
-         else if (inet_aton (host_name, &host_addr))
-           sa->sin_addr = host_addr;
-
-         else if (host_name && strlen (host_name) > 0)
-           {
-             struct hostent *host = gethostbyname (host_name);
-             if (!host)
-               error = clib_error_return (0, "unknown host `%s'", config);
-             else
-               clib_memcpy (&sa->sin_addr.s_addr, host->h_addr_list[0],
-                            host->h_length);
-           }
-
-         else
-           sa->sin_addr.s_addr = htonl (ip4_default_address);
-
-         vec_free (host_name);
-         if (error)
-           goto done;
-       }
-    }
-
-done:
-  return error;
-}
-
 static clib_error_t *
 default_socket_write (clib_socket_t * s)
 {
@@ -253,7 +151,7 @@ default_socket_read (clib_socket_t * sock, int n_bytes)
   u8 *buf;
 
   /* RX side of socket is down once end of file is reached. */
-  if (sock->flags & CLIB_SOCKET_F_RX_END_OF_FILE)
+  if (sock->rx_end_of_file)
     return 0;
 
   fd = sock->fd;
@@ -275,7 +173,7 @@ default_socket_read (clib_socket_t * sock, int n_bytes)
 
   /* Other side closed the socket. */
   if (n_read == 0)
-    sock->flags |= CLIB_SOCKET_F_RX_END_OF_FILE;
+    sock->rx_end_of_file = 1;
 
 non_fatal:
   vec_inc_len (sock->rx_buffer, n_read - n_bytes);
@@ -328,7 +226,7 @@ static clib_error_t *
 default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
                        int fds[], int num_fds)
 {
-#ifdef __linux__
+#ifdef CLIB_LINUX
   char ctl[CMSG_SPACE (sizeof (int) * num_fds) +
           CMSG_SPACE (sizeof (struct ucred))];
   struct ucred *cr = 0;
@@ -363,7 +261,7 @@ default_socket_recvmsg (clib_socket_t * s, void *msg, int msglen,
     {
       if (cmsg->cmsg_level == SOL_SOCKET)
        {
-#ifdef __linux__
+#ifdef CLIB_LINUX
          if (cmsg->cmsg_type == SCM_CREDENTIALS)
            {
              cr = (struct ucred *) CMSG_DATA (cmsg);
@@ -399,158 +297,433 @@ socket_init_funcs (clib_socket_t * s)
     s->recvmsg_func = default_socket_recvmsg;
 }
 
+static const struct
+{
+  char *prefix;
+  sa_family_t family;
+  clib_socket_type_t type;
+  u16 skip_prefix : 1;
+  u16 is_local : 1;
+} clib_socket_type_data[] = {
+  { .prefix = "unix:",
+    .family = AF_UNIX,
+    .type = CLIB_SOCKET_TYPE_UNIX,
+    .skip_prefix = 1,
+    .is_local = 1 },
+  { .prefix = "tcp:",
+    .family = AF_INET,
+    .type = CLIB_SOCKET_TYPE_INET,
+    .skip_prefix = 1 },
+  { .prefix = "abstract:",
+    .family = AF_UNIX,
+    .type = CLIB_SOCKET_TYPE_LINUX_ABSTRACT,
+    .skip_prefix = 1,
+    .is_local = 1 },
+  { .prefix = "/",
+    .family = AF_UNIX,
+    .type = CLIB_SOCKET_TYPE_UNIX,
+    .skip_prefix = 0,
+    .is_local = 1 },
+  { .prefix = "",
+    .family = AF_INET,
+    .type = CLIB_SOCKET_TYPE_INET,
+    .skip_prefix = 0,
+    .is_local = 0 },
+  { .prefix = "",
+    .family = AF_UNIX,
+    .type = CLIB_SOCKET_TYPE_UNIX,
+    .skip_prefix = 0,
+    .is_local = 1 },
+};
+
+static u8 *
+_clib_socket_get_string (char **p, int is_hostname)
+{
+  u8 *s = 0;
+  while (**p)
+    {
+      switch (**p)
+       {
+       case '_':
+         if (is_hostname)
+           return s;
+       case 'a' ... 'z':
+       case 'A' ... 'Z':
+       case '0' ... '9':
+       case '/':
+       case '-':
+       case '.':
+         vec_add1 (s, **p);
+         (*p)++;
+         break;
+         break;
+       default:
+         return s;
+       }
+    }
+  return s;
+}
+
+__clib_export int
+clib_socket_prefix_is_valid (char *s)
+{
+  for (typeof (clib_socket_type_data[0]) *d = clib_socket_type_data;
+       d - clib_socket_type_data < ARRAY_LEN (clib_socket_type_data); d++)
+    if (d->skip_prefix && strncmp (s, d->prefix, strlen (d->prefix)) == 0)
+      return 1;
+  return 0;
+}
+
 __clib_export clib_error_t *
-clib_socket_init (clib_socket_t * s)
+clib_socket_init (clib_socket_t *s)
 {
-  union
-  {
-    struct sockaddr sa;
-    struct sockaddr_un su;
-  } addr;
+  struct sockaddr_un su = { .sun_family = AF_UNIX };
+  struct sockaddr_in si = { .sin_family = AF_INET };
+  struct sockaddr *sa = 0;
+  typeof (clib_socket_type_data[0]) *data = 0;
   socklen_t addr_len = 0;
-  int socket_type, rv;
-  clib_error_t *error = 0;
-  word port;
+  int rv;
+  char *p;
+  clib_error_t *err = 0;
+  u8 *name = 0;
+  u16 port = 0;
+#if CLIB_LINUX
+  int netns_fd = -1;
+#endif
 
-  error = socket_config (s->config, &addr.sa, &addr_len,
-                        (s->flags & CLIB_SOCKET_F_IS_SERVER
-                         ? INADDR_LOOPBACK : INADDR_ANY));
-  if (error)
-    goto done;
+  s->fd = -1;
 
-  socket_init_funcs (s);
+  if (!s->config)
+    s->config = "";
+
+  for (int i = 0; i < ARRAY_LEN (clib_socket_type_data); i++)
+    {
+      typeof (clib_socket_type_data[0]) *d = clib_socket_type_data + i;
+
+      if (d->is_local == 0 && s->local_only)
+       continue;
+
+      if (strncmp (s->config, d->prefix, strlen (d->prefix)) == 0)
+       {
+         data = d;
+         break;
+       }
+    }
+
+  if (data == 0)
+    return clib_error_return (0, "unsupported socket config '%s'", s->config);
+
+  s->type = data->type;
+  p = s->config + (data->skip_prefix ? strlen (data->prefix) : 0);
 
-  socket_type = s->flags & CLIB_SOCKET_F_SEQPACKET ?
-    SOCK_SEQPACKET : SOCK_STREAM;
+  name = _clib_socket_get_string (&p, data->type == CLIB_SOCKET_TYPE_INET);
+  vec_add1 (name, 0);
 
-  s->fd = socket (addr.sa.sa_family, socket_type, 0);
-  if (s->fd < 0)
+  /* parse port type for INET sockets */
+  if (data->type == CLIB_SOCKET_TYPE_INET && p[0] == ':')
     {
-      error = clib_error_return_unix (0, "socket (fd %d, '%s')",
-                                     s->fd, s->config);
+      char *old_p = p + 1;
+      long long ll = strtoll (old_p, &p, 0);
+
+      if (p == old_p)
+       {
+         err = clib_error_return (0, "invalid port");
+         goto done;
+       }
+
+      if (ll > CLIB_U16_MAX || ll < 1)
+       {
+         err = clib_error_return (0, "port out of range");
+         goto done;
+       }
+      port = ll;
+    }
+
+  while (p[0] == ',')
+    {
+      p++;
+      if (0)
+       ;
+#if CLIB_LINUX
+      else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
+              strncmp (p, "netns_name=", 11) == 0)
+       {
+         p += 11;
+         u8 *str = _clib_socket_get_string (&p, 0);
+         u8 *pathname = format (0, "/var/run/netns/%v%c", str, 0);
+         if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
+           err = clib_error_return_unix (0, "open('%s')", pathname);
+         vec_free (str);
+         vec_free (pathname);
+         if (err)
+           goto done;
+       }
+      else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT && netns_fd == -1 &&
+              strncmp (p, "netns_pid=", 10) == 0)
+       {
+         char *old_p = p = p + 10;
+         u32 pid = (u32) strtol (old_p, &p, 0);
+
+         if (p == old_p)
+           err = clib_error_return (0, "invalid pid");
+         else
+           {
+             u8 *pathname = format (0, "/proc/%u/ns/net%c", pid, 0);
+             if ((netns_fd = open ((char *) pathname, O_RDONLY)) < 0)
+               err = clib_error_return_unix (0, "open('%s')", pathname);
+             vec_free (pathname);
+           }
+         if (err)
+           goto done;
+       }
+#endif
+      else
+       break;
+    }
+
+  if (p[0] != 0)
+    {
+      err = clib_error_return (0, "unknown input `%s'", p);
       goto done;
     }
 
-  port = 0;
-  if (addr.sa.sa_family == PF_INET)
-    port = ((struct sockaddr_in *) &addr)->sin_port;
+#if CLIB_LINUX
+  /* change netns if requested */
+  if (s->type != CLIB_SOCKET_TYPE_INET && netns_fd != -1)
+    {
+      int fd = open ("/proc/self/ns/net", O_RDONLY);
+
+      if (setns (netns_fd, CLONE_NEWNET) < 0)
+       {
+         close (fd);
+         err = clib_error_return_unix (0, "setns(%d)", netns_fd);
+         goto done;
+       }
+      netns_fd = fd;
+    }
+#endif
 
-  if (s->flags & CLIB_SOCKET_F_IS_SERVER)
+  if (s->type == CLIB_SOCKET_TYPE_INET)
     {
-      uword need_bind = 1;
+      addr_len = sizeof (si);
+      si.sin_port = htons (port);
+
+      if (name)
+       {
+         struct in_addr host_addr;
+         vec_add1 (name, 0);
+
+         /* Recognize localhost to avoid host lookup in most common cast. */
+         if (!strcmp ((char *) name, "localhost"))
+           si.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+         else if (inet_aton ((char *) name, &host_addr))
+           si.sin_addr = host_addr;
+
+         else if (strlen ((char *) name) > 0)
+           {
+             struct hostent *host = gethostbyname ((char *) name);
+             if (!host)
+               err = clib_error_return (0, "unknown host `%s'", name);
+             else
+               clib_memcpy (&si.sin_addr.s_addr, host->h_addr_list[0],
+                            host->h_length);
+           }
+
+         else
+           si.sin_addr.s_addr =
+             htonl (s->is_server ? INADDR_LOOPBACK : INADDR_ANY);
+
+         if (err)
+           goto done;
+       }
+      sa = (struct sockaddr *) &si;
+    }
+  else if (s->type == CLIB_SOCKET_TYPE_UNIX)
+    {
+      struct stat st = { 0 };
+      char *path = (char *) &su.sun_path;
 
-      if (addr.sa.sa_family == PF_INET)
+      if (vec_len (name) > sizeof (su.sun_path) - 1)
        {
-         if (port == 0)
+         err = clib_error_return (0, "File path '%v' too long", name);
+         goto done;
+       }
+
+      clib_memcpy (path, s->config, vec_len (name));
+      addr_len = sizeof (su);
+      sa = (struct sockaddr *) &su;
+
+      rv = stat (path, &st);
+      if (!s->is_server && rv < 0)
+       {
+         err = clib_error_return_unix (0, "stat ('%s')", path);
+         goto done;
+       }
+
+      if (s->is_server && rv == 0)
+       {
+         if (S_ISSOCK (st.st_mode))
            {
-             port = find_free_port (s->fd);
-             if (port < 0)
+             int client_fd = socket (AF_UNIX, SOCK_STREAM, 0);
+             int ret = connect (client_fd, (const struct sockaddr *) &su,
+                                sizeof (su));
+             typeof (errno) connect_errno = errno;
+             close (client_fd);
+
+             if (ret == 0 || (ret < 0 && connect_errno != ECONNREFUSED))
+               {
+                 err = clib_error_return (0, "Active listener on '%s'", path);
+                 goto done;
+               }
+
+             if (unlink (path) < 0)
                {
-                 error = clib_error_return (0, "no free port (fd %d, '%s')",
-                                            s->fd, s->config);
+                 err = clib_error_return_unix (0, "unlink ('%s')", path);
                  goto done;
                }
-             need_bind = 0;
            }
+         else
+           {
+             err = clib_error_return (0, "File '%s' already exists", path);
+             goto done;
+           }
+       }
+    }
+#if CLIB_LINUX
+  else if (s->type == CLIB_SOCKET_TYPE_LINUX_ABSTRACT)
+    {
+      if (vec_len (name) > sizeof (su.sun_path) - 2)
+       {
+         err = clib_error_return (0, "Socket name '%v' too long", name);
+         goto done;
+       }
+
+      clib_memcpy (&su.sun_path[1], name, vec_len (name));
+      addr_len = sizeof (su.sun_family) + vec_len (name);
+      sa = (struct sockaddr *) &su;
+      s->allow_group_write = 0;
+    }
+#endif
+  else
+    {
+      err = clib_error_return_unix (0, "unknown socket family");
+      goto done;
+    }
+
+  socket_init_funcs (s);
+
+  if ((s->fd = socket (sa->sa_family,
+                      s->is_seqpacket ? SOCK_SEQPACKET : SOCK_STREAM, 0)) < 0)
+    {
+      err =
+       clib_error_return_unix (0, "socket (fd %d, '%s')", s->fd, s->config);
+      goto done;
+    }
+
+  if (s->is_server)
+    {
+      uword need_bind = 1;
+
+      if (sa->sa_family == AF_INET && si.sin_port == 0)
+       {
+         word port = find_free_port (s->fd);
+         if (port < 0)
+           {
+             err = clib_error_return (0, "no free port (fd %d, '%s')", s->fd,
+                                      s->config);
+             goto done;
+           }
+         si.sin_port = port;
+         need_bind = 0;
        }
-      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. */
-      {
-       int v = 1;
-       if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof (v)) < 0)
-         clib_unix_warning ("setsockopt SO_REUSEADDR fails");
-      }
-
-#if __linux__
-      if (addr.sa.sa_family == PF_LOCAL && s->flags & CLIB_SOCKET_F_PASSCRED)
+
+      if (setsockopt (s->fd, SOL_SOCKET, SO_REUSEADDR, &((int){ 1 }),
+                     sizeof (int)) < 0)
+       clib_unix_warning ("setsockopt SO_REUSEADDR fails");
+
+#if CLIB_LINUX
+      if (sa->sa_family == AF_UNIX && s->passcred)
        {
-         int x = 1;
-         if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &x, sizeof (x)) < 0)
+         if (setsockopt (s->fd, SOL_SOCKET, SO_PASSCRED, &((int){ 1 }),
+                         sizeof (int)) < 0)
            {
-             error = clib_error_return_unix (0, "setsockopt (SO_PASSCRED, "
-                                             "fd %d, '%s')", s->fd,
-                                             s->config);
+             err = clib_error_return_unix (0,
+                                           "setsockopt (SO_PASSCRED, "
+                                           "fd %d, '%s')",
+                                           s->fd, s->config);
              goto done;
            }
        }
 #endif
 
-      if (need_bind && bind (s->fd, &addr.sa, addr_len) < 0)
+      if (need_bind && bind (s->fd, sa, addr_len) < 0)
        {
-         error = clib_error_return_unix (0, "bind (fd %d, '%s')",
-                                         s->fd, s->config);
+         err =
+           clib_error_return_unix (0, "bind (fd %d, '%s')", s->fd, s->config);
          goto done;
        }
 
       if (listen (s->fd, 5) < 0)
        {
-         error = clib_error_return_unix (0, "listen (fd %d, '%s')",
-                                         s->fd, s->config);
+         err = clib_error_return_unix (0, "listen (fd %d, '%s')", s->fd,
+                                       s->config);
          goto done;
        }
-      if (addr.sa.sa_family == PF_LOCAL &&
-         s->flags & CLIB_SOCKET_F_ALLOW_GROUP_WRITE &&
-         ((struct sockaddr_un *) &addr)->sun_path[0] != 0)
+
+      if (s->local_only && s->allow_group_write)
        {
-         struct stat st = { 0 };
-         if (stat (((struct sockaddr_un *) &addr)->sun_path, &st) < 0)
+         if (fchmod (s->fd, S_IWGRP) < 0)
            {
-             error = clib_error_return_unix (0, "stat (fd %d, '%s')",
-                                             s->fd, s->config);
-             goto done;
-           }
-         st.st_mode |= S_IWGRP;
-         if (chmod (((struct sockaddr_un *) &addr)->sun_path, st.st_mode) <
-             0)
-           {
-             error =
-               clib_error_return_unix (0, "chmod (fd %d, '%s', mode %o)",
-                                       s->fd, s->config, st.st_mode);
+             err = clib_error_return_unix (
+               0, "fchmod (fd %d, '%s', mode S_IWGRP)", s->fd, s->config);
              goto done;
            }
        }
     }
   else
     {
-      if ((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT)
-         && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
+      if (s->non_blocking_connect && fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
        {
-         error = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
-                                         s->fd, s->config);
+         err = clib_error_return_unix (0, "fcntl NONBLOCK (fd %d, '%s')",
+                                       s->fd, s->config);
          goto done;
        }
 
-      while ((rv = connect (s->fd, &addr.sa, addr_len)) < 0
-            && errno == EAGAIN)
+      while ((rv = connect (s->fd, sa, addr_len)) < 0 && errno == EAGAIN)
        ;
-      if (rv < 0 && !((s->flags & CLIB_SOCKET_F_NON_BLOCKING_CONNECT) &&
-                     errno == EINPROGRESS))
+      if (rv < 0 && !(s->non_blocking_connect && errno == EINPROGRESS))
        {
-         error = clib_error_return_unix (0, "connect (fd %d, '%s')",
-                                         s->fd, s->config);
+         err = 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) &&
+      if (!s->non_blocking_connect && !s->is_blocking &&
          fcntl (s->fd, F_SETFL, O_NONBLOCK) < 0)
        {
-         error = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
-                                         s->fd, s->config);
+         err = clib_error_return_unix (0, "fcntl NONBLOCK2 (fd %d, '%s')",
+                                       s->fd, s->config);
          goto done;
        }
     }
 
-  return error;
-
 done:
-  if (s->fd > 0)
-    close (s->fd);
-  return error;
+  if (err && s->fd > -1)
+    {
+      close (s->fd);
+      s->fd = -1;
+    }
+#if CLIB_LINUX
+  if (netns_fd != -1)
+    {
+      setns (CLONE_NEWNET, netns_fd);
+      close (netns_fd);
+    }
+#endif
+  vec_free (name);
+  return err;
 }
 
 __clib_export clib_error_t *
index fa5ef1e..132e89f 100644 (file)
 #include <vppinfra/error.h>
 #include <vppinfra/format.h>
 
+typedef enum
+{
+  CLIB_SOCKET_TYPE_UNKNOWN = 0,
+  CLIB_SOCKET_TYPE_INET,
+  CLIB_SOCKET_TYPE_UNIX,
+#if CLIB_LINUX
+  CLIB_SOCKET_TYPE_LINUX_ABSTRACT,
+#endif
+} clib_socket_type_t;
+
 typedef struct _socket_t
 {
   /* File descriptor. */
@@ -54,15 +64,21 @@ typedef struct _socket_t
   /* Config string for socket HOST:PORT or just HOST. */
   char *config;
 
-  u32 flags;
-#define CLIB_SOCKET_F_IS_SERVER (1 << 0)
-#define CLIB_SOCKET_F_IS_CLIENT (0 << 0)
-#define CLIB_SOCKET_F_RX_END_OF_FILE (1 << 2)
-#define CLIB_SOCKET_F_NON_BLOCKING_CONNECT (1 << 3)
-#define CLIB_SOCKET_F_ALLOW_GROUP_WRITE (1 << 4)
-#define CLIB_SOCKET_F_SEQPACKET (1 << 5)
-#define CLIB_SOCKET_F_PASSCRED  (1 << 6)
-#define CLIB_SOCKET_F_BLOCKING            (1 << 7)
+  union
+  {
+    struct
+    {
+      u32 is_server : 1;
+      u32 rx_end_of_file : 1;
+      u32 non_blocking_connect : 1;
+      u32 allow_group_write : 1;
+      u32 is_seqpacket : 1;
+      u32 passcred : 1;
+      u32 is_blocking : 1;
+      u32 local_only : 1;
+    };
+    u32 flags;
+  };
 
   /* Transmit buffer.  Holds data waiting to be written. */
   u8 *tx_buffer;
@@ -85,9 +101,18 @@ typedef struct _socket_t
                                 int fds[], int num_fds);
   clib_error_t *(*sendmsg_func) (struct _socket_t * s, void *msg, int msglen,
                                 int fds[], int num_fds);
+  clib_socket_type_t type;
   uword private_data;
 } clib_socket_t;
 
+#define CLIB_SOCKET_FLAG(f)            (((clib_socket_t){ .f = 1 }).flags)
+#define CLIB_SOCKET_F_IS_CLIENT                0
+#define CLIB_SOCKET_F_IS_SERVER                CLIB_SOCKET_FLAG (is_server)
+#define CLIB_SOCKET_F_ALLOW_GROUP_WRITE CLIB_SOCKET_FLAG (allow_group_write)
+#define CLIB_SOCKET_F_SEQPACKET                CLIB_SOCKET_FLAG (is_seqpacket)
+#define CLIB_SOCKET_F_PASSCRED         CLIB_SOCKET_FLAG (passcred)
+#define CLIB_SOCKET_F_BLOCKING         CLIB_SOCKET_FLAG (is_blocking)
+
 /* socket config format is host:port.
    Unspecified port causes a free one to be chosen starting
    from IPPORT_USERRESERVED (5000). */
@@ -98,10 +123,12 @@ clib_error_t *clib_socket_init_netns (clib_socket_t *socket, u8 *namespace);
 clib_error_t *clib_socket_accept (clib_socket_t * server,
                                  clib_socket_t * client);
 
+int clib_socket_prefix_is_valid (char *s);
+
 always_inline uword
 clib_socket_is_server (clib_socket_t * sock)
 {
-  return (sock->flags & CLIB_SOCKET_F_IS_SERVER) != 0;
+  return sock->is_server;
 }
 
 always_inline uword
@@ -120,7 +147,7 @@ clib_socket_is_connected (clib_socket_t * sock)
 always_inline int
 clib_socket_rx_end_of_file (clib_socket_t * s)
 {
-  return s->flags & CLIB_SOCKET_F_RX_END_OF_FILE;
+  return s->rx_end_of_file;
 }
 
 always_inline void *