patchpanel: apply VPN routing tag

This patch adds to patchpanel datapath service the iptables rules for:
 - Tagging with the VPN routing tag 0xfffe0000 the fwmark of all
   packets that should be routed through a VPN.
 - Saving in conntrack the VPN routing tag of all traffic routed through
   a VPN.
 - Restoring the routing tag (VPN or physical) of any established
   connection for sockets in the host namespaces.

This patch together with the shill ip rule patch with Change-Id
I534337bc6cad8b16a723c38cf9bb5d71f89bd831 effectively migrates the VPN
routing implementation from being based fully routing rules and uid
matching to being based on the new fwmark routing tags managed by
patchpanel.

BUG=b:161507671
BUG=chromium:1022028
BUG=chromium:911954
BUG=chromium:973065
TEST=Flashed rammus with patchpanel routing changes, connected multiple
types of VPNs:
  - l2tp full tunnel without Internet,
  - OpenVPN split tunnel,
  - Android VPNs full tunnel,
  - Chrome AnyConnect split tunnel,
and conducted various testing:
  - General connectivity using ping, traceroute, wget (or equivalent
  actions) on Chrome, Linux VM terminal, ARC, crosh, shell.
  - Checked DNS settings and DNS reachability.
  - Checked Internet reachability for VPNs with Internet.
  - Checked split routing for VPNs with split routing setup.
  - Checked connection pinning for Chrome, Linux VMs, ARC, shell.

Cq-Depend: chromium:2532025
Disallow-Recycled-Builds: hatch-cq
Change-Id: I2f342faac4b662610be024d62e7c18d04cddfb09
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2499121
Tested-by: Hugo Benichi <hugobenichi@google.com>
Commit-Queue: Hugo Benichi <hugobenichi@google.com>
Reviewed-by: Garrick Evans <garrick@chromium.org>
diff --git a/patchpanel/datapath_test.cc b/patchpanel/datapath_test.cc
index 0ec69e3..005e278 100644
--- a/patchpanel/datapath_test.cc
+++ b/patchpanel/datapath_test.cc
@@ -183,6 +183,17 @@
                        ElementsAre("-A", "PREROUTING", "-i", "vmtap+", "-j",
                                    "MARK", "--set-mark", "1/1", "-w"),
                        true, nullptr));
+  // Asserts for OUTPUT CONNMARK restore rule
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-A", "OUTPUT", "-j", "CONNMARK",
+                                           "--restore-mark", "--mask",
+                                           "0xffff0000", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-A", "OUTPUT", "-j", "CONNMARK",
+                                            "--restore-mark", "--mask",
+                                            "0xffff0000", "-w"),
+                                true, nullptr));
   // Asserts for apply_local_source_mark chain
   EXPECT_CALL(runner,
               iptables(StrEq("mangle"),
@@ -444,6 +455,17 @@
                                ElementsAre("-D", "OUTPUT", "-j",
                                            "apply_local_source_mark", "-w"),
                                true, nullptr));
+  // Asserts for OUTPUT CONNMARK restore rule
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-D", "OUTPUT", "-j", "CONNMARK",
+                                           "--restore-mark", "--mask",
+                                           "0xffff0000", "-w"),
+                               true, nullptr));
+  EXPECT_CALL(runner, ip6tables(StrEq("mangle"),
+                                ElementsAre("-D", "OUTPUT", "-j", "CONNMARK",
+                                            "--restore-mark", "--mask",
+                                            "0xffff0000", "-w"),
+                                true, nullptr));
   EXPECT_CALL(runner,
               iptables(StrEq("mangle"),
                        ElementsAre("-F", "apply_local_source_mark", "-w"), true,
@@ -1091,6 +1113,59 @@
   datapath.StopConnectionPinning("eth0");
 }
 
+TEST(DatapathTest, StartStopVpnRouting_NoVirtualDevices) {
+  MockProcessRunner runner;
+  MockFirewall firewall;
+
+  // Setup
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-A", "POSTROUTING", "-o", "arcbr0",
+                                           "-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", "arcbr0",
+                                            "-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));
+  // Teardown
+  EXPECT_CALL(runner, iptables(StrEq("mangle"),
+                               ElementsAre("-D", "POSTROUTING", "-o", "arcbr0",
+                                           "-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", "arcbr0",
+                                            "-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));
+
+  Datapath datapath(&runner, &firewall);
+  datapath.SetIfnameIndex("arcbr0", 5);
+  datapath.StartVpnRouting("arcbr0");
+  datapath.StopVpnRouting("arcbr0");
+}
+
 TEST(DatapathTest, AddInboundIPv4DNAT) {
   MockProcessRunner runner;
   MockFirewall firewall;