patchpanel: Add GetTrafficCounters in patchpanel client

BUG=b:160112867
TEST=unit_tests and fuzzer test;
TEST=wrote experiment code in shill::Manager to invoke this method, it
  seems to be working well.

Change-Id: Iae0c4aca2278a80917dc3f593e2142c005e0f761
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2319439
Tested-by: Jie Jiang <jiejiang@chromium.org>
Commit-Queue: Jie Jiang <jiejiang@chromium.org>
Reviewed-by: Garrick Evans <garrick@chromium.org>
Reviewed-by: Hugo Benichi <hugobenichi@google.com>
Reviewed-by: Matthew Wang <matthewmwang@chromium.org>
diff --git a/patchpanel/client.cc b/patchpanel/client.cc
index eabd51b..e5cffdd 100644
--- a/patchpanel/client.cc
+++ b/patchpanel/client.cc
@@ -437,6 +437,39 @@
   return std::make_pair(std::move(fd_local), std::move(response));
 }
 
+std::vector<TrafficCounter> Client::GetTrafficCounters(
+    const std::set<std::string>& devices) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kGetTrafficCountersMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  TrafficCountersRequest request;
+  for (const auto& device : devices) {
+    request.add_devices(device);
+  }
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode TrafficCountersRequest proto";
+    return {};
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send TrafficCountersRequest message to patchpanel "
+                  "service";
+    return {};
+  }
+
+  TrafficCountersResponse response;
+  dbus::MessageReader reader(dbus_response.get());
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse TrafficCountersResponse proto";
+    return {};
+  }
+
+  return {response.counters().begin(), response.counters().end()};
+}
+
 bool Client::ModifyPortRule(ModifyPortRuleRequest::Operation op,
                             ModifyPortRuleRequest::RuleType type,
                             ModifyPortRuleRequest::Protocol proto,
diff --git a/patchpanel/client.h b/patchpanel/client.h
index 2351f0b..ea27957 100644
--- a/patchpanel/client.h
+++ b/patchpanel/client.h
@@ -66,6 +66,13 @@
                    const std::string& outbound_ifname,
                    bool forward_user_traffic);
 
+  // Gets the traffic counters kept by patchpanel. |devices| is the set of
+  // interfaces (shill devices) for which counters should be returned, any
+  // unknown interfaces will be ignored. If |devices| is empty, counters for all
+  // known interfaces will be returned.
+  std::vector<TrafficCounter> GetTrafficCounters(
+      const std::set<std::string>& devices);
+
   // Sends a ModifyPortRuleRequest to modify iptables ingress rules.
   // This should only be called by permission_broker's 'devbroker'.
   bool ModifyPortRule(patchpanel::ModifyPortRuleRequest::Operation op,
diff --git a/patchpanel/client_fuzzer.cc b/patchpanel/client_fuzzer.cc
index d15f9a4..ae811e4 100644
--- a/patchpanel/client_fuzzer.cc
+++ b/patchpanel/client_fuzzer.cc
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <net/if.h>
+
 #include <base/logging.h>
 #include <dbus/message.h>
 #include <fuzzer/FuzzedDataProvider.h>
@@ -71,7 +73,7 @@
     client.NotifyArcVmStartup(provider.ConsumeIntegral<uint32_t>());
     client.NotifyArcVmShutdown(provider.ConsumeIntegral<uint32_t>());
     NetworkDevice device;
-    device.set_ifname(provider.ConsumeRandomLengthString(100));
+    device.set_ifname(provider.ConsumeRandomLengthString(IFNAMSIZ * 2));
     device.set_ipv4_addr(provider.ConsumeIntegral<uint32_t>());
     device.mutable_ipv4_subnet()->set_base_addr(
         provider.ConsumeIntegral<uint32_t>());
@@ -94,6 +96,14 @@
     client.ConnectNamespace(provider.ConsumeIntegral<pid_t>(),
                             provider.ConsumeRandomLengthString(100),
                             provider.ConsumeBool());
+    std::set<std::string> devices_for_counters;
+    for (int i = 0; i < 10; i++) {
+      if (provider.ConsumeBool()) {
+        devices_for_counters.insert(
+            provider.ConsumeRandomLengthString(IFNAMSIZ * 2));
+      }
+    }
+    client.GetTrafficCounters(devices_for_counters);
   }
   bus->ShutdownAndBlock();
   return 0;