vppinfra: add abstract socket & netns fns

* Add clib_socket_init support for abstract sockets
if name starts with an '@'
* Add clib_socket_init_netns to open socket in netns
* Add clib_netns_open

Type: feature

Change-Id: I89637ad657c702ec38ddecb5c03a1673d0dfb104
Signed-off-by: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
diff --git a/src/plugins/linux-cp/lcp_interface.c b/src/plugins/linux-cp/lcp_interface.c
index 3fc9117..da40961 100644
--- a/src/plugins/linux-cp/lcp_interface.c
+++ b/src/plugins/linux-cp/lcp_interface.c
@@ -14,7 +14,6 @@
  */
 
 #define _GNU_SOURCE
-#include <sched.h>
 #include <fcntl.h>
 #include <ctype.h>
 #include <sys/socket.h>
@@ -26,6 +25,8 @@
 #include <vnet/plugin/plugin.h>
 #include <vnet/plugin/plugin.h>
 
+#include <vppinfra/linux/netns.h>
+
 #include <vnet/ip/ip_punt_drop.h>
 #include <vnet/fib/fib_table.h>
 #include <vnet/adj/adj_mcast.h>
@@ -614,17 +615,6 @@
   return 1;
 }
 
-static int
-lcp_itf_get_ns_fd (char *ns_name)
-{
-  char ns_path[256] = "/proc/self/ns/net";
-
-  if (ns_name)
-    snprintf (ns_path, sizeof (ns_path) - 1, "/var/run/netns/%s", ns_name);
-
-  return open (ns_path, O_RDONLY);
-}
-
 static void
 lcp_itf_set_vif_link_state (u32 vif_index, u8 up, u8 *ns)
 {
@@ -634,13 +624,10 @@
 
   if (ns)
     {
-      u8 *ns_path = 0;
-
-      curr_ns_fd = open ("/proc/self/ns/net", O_RDONLY);
-      ns_path = format (0, "/var/run/netns/%s%c", (char *) ns, 0);
-      vif_ns_fd = open ((char *) ns_path, O_RDONLY);
+      curr_ns_fd = clib_netns_open (NULL /* self */);
+      vif_ns_fd = clib_netns_open (ns);
       if (vif_ns_fd != -1)
-	setns (vif_ns_fd, CLONE_NEWNET);
+	clib_setns (vif_ns_fd);
     }
 
   vnet_netlink_set_link_state (vif_index, up);
@@ -650,7 +637,7 @@
 
   if (curr_ns_fd != -1)
     {
-      setns (curr_ns_fd, CLONE_NEWNET);
+      clib_setns (curr_ns_fd);
       close (curr_ns_fd);
     }
 }
@@ -706,12 +693,12 @@
 
       if (ns && ns[0] != 0)
 	{
-	  orig_ns_fd = lcp_itf_get_ns_fd (NULL);
-	  ns_fd = lcp_itf_get_ns_fd ((char *) ns);
+	  orig_ns_fd = clib_netns_open (NULL /* self */);
+	  ns_fd = clib_netns_open (ns);
 	  if (orig_ns_fd == -1 || ns_fd == -1)
 	    goto socket_close;
 
-	  setns (ns_fd, CLONE_NEWNET);
+	  clib_setns (ns_fd);
 	}
 
       vif_index = if_nametoindex ((const char *) host_if_name);
@@ -745,7 +732,7 @@
     socket_close:
       if (orig_ns_fd != -1)
 	{
-	  setns (orig_ns_fd, CLONE_NEWNET);
+	  clib_setns (orig_ns_fd);
 	  close (orig_ns_fd);
 	}
       if (ns_fd != -1)
diff --git a/src/vnet/devices/tap/tap.c b/src/vnet/devices/tap/tap.c
index dfd3258..33d6e3b 100644
--- a/src/vnet/devices/tap/tap.c
+++ b/src/vnet/devices/tap/tap.c
@@ -27,7 +27,6 @@
 #include <linux/sockios.h>
 #include <sys/eventfd.h>
 #include <net/if_arp.h>
-#include <sched.h>
 #include <limits.h>
 
 #include <linux/netlink.h>
@@ -36,6 +35,7 @@
 #include <vlib/vlib.h>
 #include <vlib/physmem.h>
 #include <vlib/unix/unix.h>
+#include <vppinfra/linux/netns.h>
 #include <vnet/ethernet/ethernet.h>
 #include <vnet/ip/ip4_packet.h>
 #include <vnet/ip/ip6_packet.h>
@@ -79,24 +79,6 @@
   return 0;
 }
 
-static int
-open_netns_fd (char *netns)
-{
-  u8 *s = 0;
-  int fd;
-
-  if (strncmp (netns, "pid:", 4) == 0)
-    s = format (0, "/proc/%u/ns/net%c", atoi (netns + 4), 0);
-  else if (netns[0] == '/')
-    s = format (0, "%s%c", netns, 0);
-  else
-    s = format (0, "/var/run/netns/%s%c", netns, 0);
-
-  fd = open ((char *) s, O_RDONLY);
-  vec_free (s);
-  return fd;
-}
-
 #define TAP_MAX_INSTANCE 1024
 
 static void
@@ -227,15 +209,15 @@
 	}
       if (args->host_namespace)
 	{
-	  old_netns_fd = open ("/proc/self/ns/net", O_RDONLY);
-	  if ((nfd = open_netns_fd ((char *) args->host_namespace)) == -1)
+	  old_netns_fd = clib_netns_open (NULL /* self */);
+	  if ((nfd = clib_netns_open (args->host_namespace)) == -1)
 	    {
 	      args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
-	      args->error = clib_error_return_unix (0, "open_netns_fd '%s'",
+	      args->error = clib_error_return_unix (0, "clib_netns_open '%s'",
 						    args->host_namespace);
 	      goto error;
 	    }
-	  if (setns (nfd, CLONE_NEWNET) == -1)
+	  if (clib_setns (nfd) == -1)
 	    {
 	      args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
 	      args->error = clib_error_return_unix (0, "setns '%s'",
@@ -423,11 +405,11 @@
          after we change our net namespace */
       if (args->host_namespace)
 	{
-	  old_netns_fd = open ("/proc/self/ns/net", O_RDONLY);
-	  if ((nfd = open_netns_fd ((char *) args->host_namespace)) == -1)
+	  old_netns_fd = clib_netns_open (NULL /* self */);
+	  if ((nfd = clib_netns_open (args->host_namespace)) == -1)
 	    {
 	      args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
-	      args->error = clib_error_return_unix (0, "open_netns_fd '%s'",
+	      args->error = clib_error_return_unix (0, "clib_netns_open '%s'",
 						    args->host_namespace);
 	      goto error;
 	    }
@@ -438,7 +420,7 @@
 	      args->rv = VNET_API_ERROR_NETLINK_ERROR;
 	      goto error;
 	    }
-	  if (setns (nfd, CLONE_NEWNET) == -1)
+	  if (clib_setns (nfd) == -1)
 	    {
 	      args->rv = VNET_API_ERROR_SYSCALL_ERROR_3;
 	      args->error = clib_error_return_unix (0, "setns '%s'",
@@ -567,7 +549,7 @@
   /* switch back to old net namespace */
   if (args->host_namespace)
     {
-      if (setns (old_netns_fd, CLONE_NEWNET) == -1)
+      if (clib_setns (old_netns_fd) == -1)
 	{
 	  args->rv = VNET_API_ERROR_SYSCALL_ERROR_2;
 	  args->error = clib_error_return_unix (0, "setns '%s'",
@@ -1065,13 +1047,13 @@
 
   if (vif->net_ns)
     {
-      old_netns_fd = open ("/proc/self/ns/net", O_RDONLY);
-      if ((nfd = open_netns_fd ((char *) vif->net_ns)) == -1)
+      old_netns_fd = clib_netns_open (NULL /* self */);
+      if ((nfd = clib_netns_open (vif->net_ns)) == -1)
 	{
 	  clib_warning ("Cannot open netns");
 	  goto done;
 	}
-      if (setns (nfd, CLONE_NEWNET) == -1)
+      if (clib_setns (nfd) == -1)
 	{
 	  clib_warning ("Cannot set ns");
 	  goto done;
@@ -1109,7 +1091,7 @@
 done:
   if (old_netns_fd != -1)
     {
-      if (setns (old_netns_fd, CLONE_NEWNET) == -1)
+      if (clib_setns (old_netns_fd) == -1)
 	{
 	  clib_warning ("Cannot set old ns");
 	}
diff --git a/src/vppinfra/CMakeLists.txt b/src/vppinfra/CMakeLists.txt
index 728072c..c682d70 100644
--- a/src/vppinfra/CMakeLists.txt
+++ b/src/vppinfra/CMakeLists.txt
@@ -204,6 +204,7 @@
     elf_clib.c
     linux/mem.c
     linux/sysfs.c
+    linux/netns.c
    )
 endif()
 
diff --git a/src/vppinfra/linux/netns.c b/src/vppinfra/linux/netns.c
new file mode 100644
index 0000000..2bd62bd
--- /dev/null
+++ b/src/vppinfra/linux/netns.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sched.h>
+
+#include <vppinfra/format.h>
+
+__clib_export int
+clib_netns_open (u8 *netns_u8)
+{
+  char *netns = (char *) netns_u8;
+  u8 *s = 0;
+  int fd;
+
+  if ((NULL) == netns)
+    s = format (0, "/proc/self/ns/net");
+  else if (strncmp (netns, "pid:", 4) == 0)
+    s = format (0, "/proc/%u/ns/net%c", atoi (netns + 4), 0);
+  else if (netns[0] == '/')
+    s = format (0, "%s%c", netns, 0);
+  else
+    s = format (0, "/var/run/netns/%s%c", netns, 0);
+
+  fd = open ((char *) s, O_RDONLY);
+  vec_free (s);
+  return fd;
+}
+
+__clib_export int
+clib_setns (int nfd)
+{
+  return setns (nfd, CLONE_NEWNET);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vppinfra/linux/netns.h b/src/vppinfra/linux/netns.h
new file mode 100644
index 0000000..5a09460
--- /dev/null
+++ b/src/vppinfra/linux/netns.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2021 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef included_vppinfra_netns_h
+#define included_vppinfra_netns_h
+
+#include <vppinfra/clib.h>
+
+int clib_netns_open (u8 *netns);
+int clib_setns (int nfd);
+
+#endif /* included_vppinfra_netns_h */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/vppinfra/socket.c b/src/vppinfra/socket.c
index d842785..26427d9 100644
--- a/src/vppinfra/socket.c
+++ b/src/vppinfra/socket.c
@@ -52,6 +52,7 @@
 #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>
 
@@ -113,6 +114,18 @@
       *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
     {
@@ -440,7 +453,8 @@
 	      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. */
@@ -477,8 +491,9 @@
 					  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)
@@ -539,6 +554,38 @@
 }
 
 __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;
+
+  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);
+
+  return error;
+}
+
+__clib_export clib_error_t *
 clib_socket_accept (clib_socket_t * server, clib_socket_t * client)
 {
   clib_error_t *err = 0;
diff --git a/src/vppinfra/socket.h b/src/vppinfra/socket.h
index 78a56fe..fa5ef1e 100644
--- a/src/vppinfra/socket.h
+++ b/src/vppinfra/socket.h
@@ -93,6 +93,8 @@
    from IPPORT_USERRESERVED (5000). */
 clib_error_t *clib_socket_init (clib_socket_t * socket);
 
+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);