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_test.cc b/patchpanel/datapath_test.cc
index ae76b37..19cf8d7 100644
--- a/patchpanel/datapath_test.cc
+++ b/patchpanel/datapath_test.cc
@@ -118,6 +118,8 @@
                int(const std::string& key,
                    const std::string& value,
                    bool log_failures));
+  MOCK_METHOD2(ip_netns_add,
+               int(const std::string& netns_name, bool log_failures));
   MOCK_METHOD3(ip_netns_attach,
                int(const std::string& netns_name,
                    pid_t netns_pid,
@@ -171,6 +173,11 @@
   EXPECT_CALL(runner, sysctl_w(StrEq(key), StrEq(value), _));
 }
 
+void Verify_ip_netns_add(MockProcessRunner& runner,
+                         const std::string& netns_name) {
+  EXPECT_CALL(runner, ip_netns_add(StrEq(netns_name), _));
+}
+
 void Verify_ip_netns_attach(MockProcessRunner& runner,
                             const std::string& netns_name,
                             pid_t pid) {
@@ -645,6 +652,33 @@
   datapath.StopRoutingNamespace(nsinfo);
 }
 
+TEST(DatapathTest, StartRoutingNewNamespace) {
+  MockProcessRunner runner;
+  MockFirewall firewall;
+  MacAddress mac = {1, 2, 3, 4, 5, 6};
+
+  // The running may fail at checking ScopedNS.IsValid() in
+  // Datapath::ConnectVethPair(), so we only check if `ip netns add` is invoked
+  // correctly here.
+  Verify_ip_netns_add(runner, "netns_foo");
+
+  ConnectedNamespace nsinfo = {};
+  nsinfo.pid = ConnectedNamespace::kNewNetnsPid;
+  nsinfo.netns_name = "netns_foo";
+  nsinfo.source = TrafficSource::USER;
+  nsinfo.outbound_ifname = "";
+  nsinfo.route_on_vpn = true;
+  nsinfo.host_ifname = "arc_ns0";
+  nsinfo.peer_ifname = "veth0";
+  nsinfo.peer_subnet = std::make_unique<Subnet>(Ipv4Addr(100, 115, 92, 128), 30,
+                                                base::DoNothing());
+  nsinfo.peer_mac_addr = mac;
+  Datapath datapath(&runner, &firewall, (ioctl_t)ioctl_rtentry_cap);
+  datapath.StartRoutingNamespace(nsinfo);
+  ioctl_reqs.clear();
+  ioctl_rtentry_args.clear();
+}
+
 TEST(DatapathTest, StartRoutingDevice_Arc) {
   MockProcessRunner runner;
   MockFirewall firewall;