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/datapath.cc b/patchpanel/datapath.cc
index 7335a25..caf6d9f 100644
--- a/patchpanel/datapath.cc
+++ b/patchpanel/datapath.cc
@@ -340,7 +340,11 @@
   // did not exit cleanly.
   if (process_runner_->ip_netns_delete(netns_name, false /*log_failures*/) == 0)
     LOG(INFO) << "Deleted left over network namespace name " << netns_name;
-  return process_runner_->ip_netns_attach(netns_name, netns_pid) == 0;
+
+  if (netns_pid == ConnectedNamespace::kNewNetnsPid)
+    return process_runner_->ip_netns_add(netns_name) == 0;
+  else
+    return process_runner_->ip_netns_attach(netns_name, netns_pid) == 0;
 }
 
 bool Datapath::NetnsDeleteName(const std::string& netns_name) {
@@ -520,8 +524,8 @@
 
   // Configure the remote veth in namespace |netns_name|.
   {
-    ScopedNS ns(netns_pid, ScopedNS::Type::Network);
-    if (!ns.IsValid() && netns_pid != kTestPID) {
+    auto ns = ScopedNS::EnterNetworkNS(netns_name);
+    if (!ns && netns_pid != kTestPID) {
       LOG(ERROR)
           << "Cannot create virtual link -- invalid container namespace?";
       return false;
@@ -597,7 +601,8 @@
 
 bool Datapath::StartRoutingNamespace(const ConnectedNamespace& nsinfo) {
   // Veth interface configuration and client routing configuration:
-  //  - attach a name to the client namespace.
+  //  - attach a name to the client namespace (or create a new named namespace
+  //    if no client is specified).
   //  - create veth pair across the current namespace and the client namespace.
   //  - configure IPv4 address on remote veth inside client namespace.
   //  - configure IPv4 address on local veth inside host namespace.
@@ -630,8 +635,8 @@
   }
 
   {
-    ScopedNS ns(nsinfo.pid, ScopedNS::Type::Network);
-    if (!ns.IsValid() && nsinfo.pid != kTestPID) {
+    auto ns = ScopedNS::EnterNetworkNS(nsinfo.netns_name);
+    if (!ns && nsinfo.pid != kTestPID) {
       LOG(ERROR) << "Invalid namespace pid " << nsinfo.pid;
       RemoveInterface(nsinfo.host_ifname);
       NetnsDeleteName(nsinfo.netns_name);