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.h b/patchpanel/scoped_ns.h
index a77a8f1..ab25180 100644
--- a/patchpanel/scoped_ns.h
+++ b/patchpanel/scoped_ns.h
@@ -5,6 +5,9 @@
 #ifndef PATCHPANEL_SCOPED_NS_H_
 #define PATCHPANEL_SCOPED_NS_H_
 
+#include <memory>
+#include <string>
+
 #include <base/files/scoped_file.h>
 #include <base/macros.h>
 
@@ -14,21 +17,24 @@
 // namespace.
 class ScopedNS {
  public:
-  enum Type {
-    Network = 1,
-    Mount,
-  };
+  // Records the current mount (network) namespace and enters another namespace
+  // identified by the input argument. Will go back to the current namespace if
+  // the returned object goes out of scope. Returns nullptr on failure.
+  static std::unique_ptr<ScopedNS> EnterMountNS(pid_t pid);
+  static std::unique_ptr<ScopedNS> EnterNetworkNS(pid_t pid);
+  static std::unique_ptr<ScopedNS> EnterNetworkNS(
+      const std::string& netns_name);
 
-  explicit ScopedNS(pid_t pid, Type type);
   ScopedNS(const ScopedNS&) = delete;
   ScopedNS& operator=(const ScopedNS&) = delete;
 
   ~ScopedNS();
 
-  // Returns whether or not the object was able to enter the target namespace.
-  bool IsValid() const { return valid_; }
-
  private:
+  ScopedNS(int nstype,
+           const std::string& current_ns_path,
+           const std::string& target_ns_path);
+
   int nstype_;
   bool valid_;
   base::ScopedFD ns_fd_;