patchpanel: add traffic accounting chains for VPNs

This patch adds traffic accounting chains and rules for traffic routed
through a VPN connection. All traffic routed to a VPN is counted with
the same chain, regardless of the type of VPN client.

BUG=b:160112868
TEST=Unit tests. Checked GetTrafficCounters still work with:
  $ dbus-send --system --dest=org.chromium.PatchPanel --print-reply \
    /org/chromium/PatchPanel org.chromium.PatchPanel.GetTrafficCounters

Change-Id: I3f0e7feabea5a18f164b1fc78ef9628da4b353eb
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2562983
Commit-Queue: Hugo Benichi <hugobenichi@google.com>
Tested-by: Hugo Benichi <hugobenichi@google.com>
Reviewed-by: Taoyu Li <taoyl@chromium.org>
diff --git a/patchpanel/counters_service_test.cc b/patchpanel/counters_service_test.cc
index 1c74442..313620a 100644
--- a/patchpanel/counters_service_test.cc
+++ b/patchpanel/counters_service_test.cc
@@ -4,8 +4,9 @@
 
 #include "patchpanel/counters_service.h"
 
-#include <memory>
 #include <net/if.h>
+
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -13,11 +14,12 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "patchpanel/fake_shill_client.h"
 #include "patchpanel/mock_firewall.h"
 
 namespace patchpanel {
 
+using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::ContainerEq;
 using ::testing::Contains;
 using ::testing::DoAll;
@@ -155,10 +157,9 @@
 class CountersServiceTest : public testing::Test {
  protected:
   void SetUp() override {
-    fake_shill_client_ = shill_helper_.FakeClient();
     datapath_ = std::make_unique<Datapath>(&runner_, &firewall_);
-    counters_svc_ = std::make_unique<CountersService>(
-        fake_shill_client_.get(), datapath_.get(), &runner_);
+    counters_svc_ =
+        std::make_unique<CountersService>(datapath_.get(), &runner_);
   }
 
   // Makes `iptables` returning a bad |output|. Expects an empty map from
@@ -189,15 +190,168 @@
     EXPECT_THAT(actual, ContainerEq(expected));
   }
 
-  FakeShillClientHelper shill_helper_;
   MockProcessRunner runner_;
   MockFirewall firewall_;
-  std::unique_ptr<FakeShillClient> fake_shill_client_;
   std::unique_ptr<Datapath> datapath_;
   std::unique_ptr<CountersService> counters_svc_;
 };
 
-TEST_F(CountersServiceTest, OnNewDevice) {
+TEST_F(CountersServiceTest, OnCreation_WithNoDevices) {
+  // The following commands are expected when eth0 comes up.
+  const std::vector<std::vector<std::string>> expected_calls{
+      {"-N", "rx_vpn", "-w"},
+      {"-N", "tx_vpn", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+  };
+
+  for (const auto& rule : expected_calls) {
+    EXPECT_CALL(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
+    EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
+  }
+
+  counters_svc_->Init({});
+}
+
+TEST_F(CountersServiceTest, OnCreation_WithDevices) {
+  // The following commands are expected when eth0 comes up.
+  const std::vector<std::vector<std::string>> expected_calls{
+      {"-N", "rx_vpn", "-w"},
+      {"-N", "tx_vpn", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-N", "rx_wlan0", "-w"},
+      {"-N", "tx_wlan0", "-w"},
+      {"-A", "INPUT", "-i", "wlan0", "-j", "rx_wlan0", "-w"},
+      {"-A", "FORWARD", "-i", "wlan0", "-j", "rx_wlan0", "-w"},
+      {"-A", "POSTROUTING", "-o", "wlan0", "-j", "tx_wlan0", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_wlan0", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_wlan0", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+  };
+
+  for (const auto& rule : expected_calls) {
+    EXPECT_CALL(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
+    EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
+  }
+
+  counters_svc_->Init({"wlan0"});
+}
+
+TEST_F(CountersServiceTest, OnPhysicalDeviceAdded) {
   // The following commands are expected when eth0 comes up.
   const std::vector<std::vector<std::string>> expected_calls{
       {"-N", "rx_eth0", "-w"},
@@ -252,9 +406,66 @@
     EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
   }
 
-  std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
-  fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
-                                                  brillo::Any(devices));
+  counters_svc_->OnPhysicalDeviceAdded("eth0");
+}
+
+TEST_F(CountersServiceTest, OnVpnDeviceAdded) {
+  // The following commands are expected when eth0 comes up.
+  const std::vector<std::vector<std::string>> expected_calls{
+      {"-N", "rx_vpn", "-w"},
+      {"-N", "tx_vpn", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "tx_vpn", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "rx_vpn", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
+       "RETURN", "-w"},
+      {"-A", "FORWARD", "-i", "tun0", "-j", "rx_vpn", "-w"},
+      {"-A", "INPUT", "-i", "tun0", "-j", "rx_vpn", "-w"},
+      {"-A", "POSTROUTING", "-o", "tun0", "-j", "tx_vpn", "-w"},
+  };
+
+  for (const auto& rule : expected_calls) {
+    EXPECT_CALL(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
+    EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
+  }
+
+  counters_svc_->Init({});
+  counters_svc_->OnVpnDeviceAdded("tun0");
 }
 
 TEST_F(CountersServiceTest, OnSameDeviceAppearAgain) {
@@ -272,9 +483,7 @@
   EXPECT_CALL(runner_, iptables(_, Contains("-I"), _, _)).Times(0);
   EXPECT_CALL(runner_, ip6tables(_, Contains("-I"), _, _)).Times(0);
 
-  std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
-  fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
-                                                  brillo::Any(devices));
+  counters_svc_->OnPhysicalDeviceAdded("eth0");
 }
 
 TEST_F(CountersServiceTest, ChainNameLength) {
@@ -289,10 +498,7 @@
       .Times(AnyNumber());
 
   static const std::string kLongInterfaceName(IFNAMSIZ, 'a');
-  std::vector<dbus::ObjectPath> devices = {
-      dbus::ObjectPath("/device/" + kLongInterfaceName)};
-  fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
-                                                  brillo::Any(devices));
+  counters_svc_->OnPhysicalDeviceAdded(kLongInterfaceName);
 }
 
 TEST_F(CountersServiceTest, QueryTrafficCounters) {