patchpanel: Support open a new netns via ConnectNamespace

Currently the ConnectNamespace API exposed by patchpanel via d-bus only
supports passing in a pid of a process and doing "ConnectNamespace" for
the netns associated with this process. While in the tast tests,
sometimes we need to open a new netns directly, and execute processes in
the created netns.

For this usage, this patch modifies the ConnectNamespace API so that
patchpanel accepts passing a special pid (i.e., pid==-1) to indicates
the client wants a new netns, invokes `ip netns add` for this case, and
returns the name of the created netns.

BUG=b:185210339
TEST=unit_tests;
TEST=Used this API in test VPN tast test, verified it worked;
TEST=Checked playstore still worked.

Change-Id: I3bbfab89df24899127e6087b0c0533e2c96037dc
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2896672
Reviewed-by: Garrick Evans <garrick@chromium.org>
Reviewed-by: Hugo Benichi <hugobenichi@google.com>
Tested-by: Jie Jiang <jiejiang@chromium.org>
Commit-Queue: Jie Jiang <jiejiang@chromium.org>
diff --git a/patchpanel/scoped_ns.cc b/patchpanel/scoped_ns.cc
index 431deaf..3e4585e 100644
--- a/patchpanel/scoped_ns.cc
+++ b/patchpanel/scoped_ns.cc
@@ -6,32 +6,45 @@
 
 #include <fcntl.h>
 #include <sched.h>
-#include <sys/types.h>
 #include <sys/stat.h>
+#include <sys/types.h>
 
 #include <string>
+#include <utility>
+
+#include <base/memory/ptr_util.h>
 
 namespace patchpanel {
 
-ScopedNS::ScopedNS(pid_t pid, Type type) : valid_(false) {
-  std::string current_ns_path;
-  std::string target_ns_path;
-  switch (type) {
-    case Mount:
-      nstype_ = CLONE_NEWNS;
-      current_ns_path = "/proc/self/ns/mnt";
-      target_ns_path = "/proc/" + std::to_string(pid) + "/ns/mnt";
-      break;
-    case Network:
-      nstype_ = CLONE_NEWNET;
-      current_ns_path = "/proc/self/ns/net";
-      target_ns_path = "/proc/" + std::to_string(pid) + "/ns/net";
-      break;
-    default:
-      LOG(ERROR) << "Unsupported namespace type " << type;
-      return;
-  }
+std::unique_ptr<ScopedNS> ScopedNS::EnterMountNS(pid_t pid) {
+  int nstype = CLONE_NEWNS;
+  const std::string current_path = "/proc/self/ns/mnt";
+  const std::string target_path = "/proc/" + std::to_string(pid) + "/ns/mnt";
+  auto ns = base::WrapUnique(new ScopedNS(nstype, current_path, target_path));
+  return ns->valid_ ? std::move(ns) : nullptr;
+}
 
+std::unique_ptr<ScopedNS> ScopedNS::EnterNetworkNS(pid_t pid) {
+  int nstype = CLONE_NEWNET;
+  const std::string current_path = "/proc/self/ns/net";
+  const std::string target_path = "/proc/" + std::to_string(pid) + "/ns/net";
+  auto ns = base::WrapUnique(new ScopedNS(nstype, current_path, target_path));
+  return ns->valid_ ? std::move(ns) : nullptr;
+}
+
+std::unique_ptr<ScopedNS> ScopedNS::EnterNetworkNS(
+    const std::string& netns_name) {
+  int nstype = CLONE_NEWNET;
+  const std::string current_path = "/proc/self/ns/net";
+  const std::string target_path = "/run/netns/" + netns_name;
+  auto ns = base::WrapUnique(new ScopedNS(nstype, current_path, target_path));
+  return ns->valid_ ? std::move(ns) : nullptr;
+}
+
+ScopedNS::ScopedNS(int nstype,
+                   const std::string& current_ns_path,
+                   const std::string& target_ns_path)
+    : nstype_(nstype), valid_(false) {
   ns_fd_.reset(open(target_ns_path.c_str(), O_RDONLY | O_CLOEXEC));
   if (!ns_fd_.is_valid()) {
     PLOG(ERROR) << "Could not open namespace " << target_ns_path;