patchpanel: route arcbr0 through non-ARC VPNs

This patch sets ARC's arcbr0 routing tags so that traffic originated
through arcbr0 by ARC is routed through any non-ARC VPN connection.

Inside ARC, arcbr0 is used to expose a fake "facade" VPN network
corresponding to the non-ARC VPN established on the host environment.

BUG=b:161507671
BUG=b:161508179
BUG=chromium:1157515
TEST=unit tests. Checked that ARC traffic is correctly routed on the
host when a full tunnel VPN connection is set up on the host.

Change-Id: I7375af1a604bfc6ecb8a729decb55b13a8717af0
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2586457
Tested-by: Hugo Benichi <hugobenichi@google.com>
Commit-Queue: Hugo Benichi <hugobenichi@google.com>
Reviewed-by: Taoyu Li <taoyl@chromium.org>
diff --git a/patchpanel/datapath_test.cc b/patchpanel/datapath_test.cc
index 13bf505..7ed6821 100644
--- a/patchpanel/datapath_test.cc
+++ b/patchpanel/datapath_test.cc
@@ -1277,7 +1277,7 @@
   datapath.StopConnectionPinning("eth0");
 }
 
-TEST(DatapathTest, StartStopVpnRouting_NoVirtualDevices) {
+TEST(DatapathTest, StartStopVpnRouting_ArcVpn) {
   MockProcessRunner runner;
   MockFirewall firewall;
 
@@ -1370,6 +1370,157 @@
   datapath.StopVpnRouting("arcbr0");
 }
 
+TEST(DatapathTest, StartStopVpnRouting_HostVpn) {
+  MockProcessRunner runner;
+  MockFirewall firewall;
+
+  // Setup
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-A", "POSTROUTING", "-o", "tun0",
+                                           "-j", "CONNMARK", "--set-mark",
+                                           "0x03ed0000/0xffff0000", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner,
+              iptables(StrEq("mangle"),
+                       ElementsAre("-A", "apply_vpn_mark", "-j", "MARK",
+                                   "--set-mark", "0x03ed0000/0xffff0000", "-w"),
+                       true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-A", "POSTROUTING", "-o", "tun0",
+                                            "-j", "CONNMARK", "--set-mark",
+                                            "0x03ed0000/0xffff0000", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-A", "apply_vpn_mark", "-j",
+                                            "MARK", "--set-mark",
+                                            "0x03ed0000/0xffff0000", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-A", "POSTROUTING", "-o", "tun0",
+                                           "-j", "CONNMARK", "--save-mark",
+                                           "--mask", "0x00003f00", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-A", "POSTROUTING", "-o", "tun0",
+                                            "-j", "CONNMARK", "--save-mark",
+                                            "--mask", "0x00003f00", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-A", "PREROUTING", "-i", "tun0",
+                                           "-j", "CONNMARK", "--restore-mark",
+                                           "--mask", "0x00003f00", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-A", "PREROUTING", "-i", "tun0",
+                                            "-j", "CONNMARK", "--restore-mark",
+                                            "--mask", "0x00003f00", "-w"),
+                                true, nullptr));
+  // Teardown
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-D", "POSTROUTING", "-o", "tun0",
+                                           "-j", "CONNMARK", "--set-mark",
+                                           "0x03ed0000/0xffff0000", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner,
+              iptables(StrEq("mangle"),
+                       ElementsAre("-D", "apply_vpn_mark", "-j", "MARK",
+                                   "--set-mark", "0x03ed0000/0xffff0000", "-w"),
+                       true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-D", "POSTROUTING", "-o", "tun0",
+                                            "-j", "CONNMARK", "--set-mark",
+                                            "0x03ed0000/0xffff0000", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-D", "apply_vpn_mark", "-j",
+                                            "MARK", "--set-mark",
+                                            "0x03ed0000/0xffff0000", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-D", "POSTROUTING", "-o", "tun0",
+                                           "-j", "CONNMARK", "--save-mark",
+                                           "--mask", "0x00003f00", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-D", "POSTROUTING", "-o", "tun0",
+                                            "-j", "CONNMARK", "--save-mark",
+                                            "--mask", "0x00003f00", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-D", "PREROUTING", "-i", "tun0",
+                                           "-j", "CONNMARK", "--restore-mark",
+                                           "--mask", "0x00003f00", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-D", "PREROUTING", "-i", "tun0",
+                                            "-j", "CONNMARK", "--restore-mark",
+                                            "--mask", "0x00003f00", "-w"),
+                                true, nullptr));
+  // Start tun0 <-> arcbr0 routing
+  EXPECT_CALL(runner, iptables(StrEq("filter"),
+                               ElementsAre("-A", "FORWARD", "-i", "tun0", "-o",
+                                           "arcbr0", "-j", "ACCEPT", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("filter"),
+                               ElementsAre("-A", "FORWARD", "-i", "arcbr0",
+                                           "-o", "tun0", "-j", "ACCEPT", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-A", "PREROUTING", "-i", "arcbr0",
+                                           "-j", "MARK", "--set-mark",
+                                           "0x00002000/0x00003f00", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-A", "PREROUTING", "-i", "arcbr0",
+                                           "-j", "MARK", "--set-mark",
+                                           "0x03ed0000/0xffff0000", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-A", "PREROUTING", "-i", "arcbr0",
+                                            "-j", "MARK", "--set-mark",
+                                            "0x00002000/0x00003f00", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-A", "PREROUTING", "-i", "arcbr0",
+                                            "-j", "MARK", "--set-mark",
+                                            "0x03ed0000/0xffff0000", "-w"),
+                                true, nullptr));
+  // Stop tun0 <-> arcbr0 routing
+  EXPECT_CALL(runner, iptables(StrEq("filter"),
+                               ElementsAre("-D", "FORWARD", "-i", "tun0", "-o",
+                                           "arcbr0", "-j", "ACCEPT", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("filter"),
+                               ElementsAre("-D", "FORWARD", "-i", "arcbr0",
+                                           "-o", "tun0", "-j", "ACCEPT", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-D", "PREROUTING", "-i", "arcbr0",
+                                           "-j", "MARK", "--set-mark",
+                                           "0x00002000/0x00003f00", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-D", "PREROUTING", "-i", "arcbr0",
+                                           "-j", "MARK", "--set-mark",
+                                           "0x03ed0000/0xffff0000", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-D", "PREROUTING", "-i", "arcbr0",
+                                            "-j", "MARK", "--set-mark",
+                                            "0x00002000/0x00003f00", "-w"),
+                                true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-D", "PREROUTING", "-i", "arcbr0",
+                                            "-j", "MARK", "--set-mark",
+                                            "0x03ed0000/0xffff0000", "-w"),
+                                true, nullptr));
+
+  Datapath datapath(&runner, &firewall);
+  datapath.SetIfnameIndex("tun0", 5);
+  datapath.StartVpnRouting("tun0");
+  datapath.StopVpnRouting("tun0");
+}
+
 TEST(DatapathTest, AddInboundIPv4DNAT) {
   MockProcessRunner runner;
   MockFirewall firewall;