blob: 188c30fe66cfec8c5128286b0eb9b2251ec2878d [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "patchpanel/counters_service.h"
#include <net/if.h>
#include <memory>
#include <string>
#include <vector>
#include <base/logging.h>
#include <chromeos/dbus/service_constants.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "patchpanel/mock_datapath.h"
namespace patchpanel {
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Contains;
using ::testing::Each;
using ::testing::ElementsAreArray;
using ::testing::Lt;
using ::testing::Return;
using ::testing::SizeIs;
using Counter = CountersService::Counter;
using CounterKey = CountersService::CounterKey;
// The following four functions should be put outside the anonymous namespace
// otherwise they could not be found in the tests.
std::ostream& operator<<(std::ostream& os, const Counter& counter) {
os << "rx_bytes:" << counter.rx_bytes << ", rx_packets:" << counter.rx_packets
<< ", tx_bytes:" << counter.tx_bytes
<< ", tx_packets:" << counter.tx_packets;
return os;
}
std::ostream& operator<<(std::ostream& os, const CounterKey& key) {
os << "ifname:" << key.ifname
<< ", source:" << TrafficCounter::Source_Name(key.source)
<< ", ip_family:" << TrafficCounter::IpFamily_Name(key.ip_family);
return os;
}
bool operator==(const CountersService::CounterKey lhs,
const CountersService::CounterKey rhs) {
return lhs.ifname == rhs.ifname && lhs.source == rhs.source &&
lhs.ip_family == rhs.ip_family;
}
bool operator==(const CountersService::Counter lhs,
const CountersService::Counter rhs) {
return lhs.rx_bytes == rhs.rx_bytes && lhs.rx_packets == rhs.rx_packets &&
lhs.tx_bytes == rhs.tx_bytes && lhs.tx_packets == rhs.tx_packets;
}
namespace {
// The following string is copied from the real output of iptables v1.6.2 by
// `iptables -t mangle -L -x -v -n`. This output contains all the accounting
// chains/rules for eth0 and wlan0.
const char kIptablesOutput[] = R"(
Chain PREROUTING (policy ACCEPT 22785 packets, 136093545 bytes)
pkts bytes target prot opt in out source destination
18 2196 MARK all -- arcbr0 * 0.0.0.0/0 0.0.0.0/0 MARK set 0x1
0 0 MARK all -- vmtap+ * 0.0.0.0/0 0.0.0.0/0 MARK set 0x1
6526 68051766 MARK all -- arc_eth0 * 0.0.0.0/0 0.0.0.0/0 MARK set 0x1
9 1104 MARK all -- arc_wlan0 * 0.0.0.0/0 0.0.0.0/0 MARK set 0x1
Chain INPUT (policy ACCEPT 4421 packets, 2461233 bytes)
pkts bytes target prot opt in out source destination
312491 1767147156 rx_eth0 all -- eth0 * 0.0.0.0/0 0.0.0.0/0
0 0 rx_wlan0 all -- wlan0 * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy ACCEPT 18194 packets, 133612816 bytes)
pkts bytes target prot opt in out source destination
6511 68041668 tx_eth0 all -- * eth0 0.0.0.0/0 0.0.0.0/0
11683 65571148 rx_eth0 all -- eth0 * 0.0.0.0/0 0.0.0.0/0
Chain OUTPUT (policy ACCEPT 4574 packets, 2900995 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 22811 packets, 136518827 bytes)
pkts bytes target prot opt in out source destination
202160 1807550291 tx_eth0 all -- * eth0 0.0.0.0/0 0.0.0.0/0 owner socket exists
2 96 tx_wlan0 all -- * wlan0 0.0.0.0/0 0.0.0.0/0 owner socket exists
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
1366 244427 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x200/0x3f00
20 1670 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x300/0x3f00
550 138402 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x500/0x3f00
5374 876172 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2000/0x3f00
39 2690 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2400/0x3f00
4 123 all -- * * 0.0.0.0/0 0.0.0.0/0
Chain tx_wlan0 (1 references)
pkts bytes target prot opt in out source destination
310 57004 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x200/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x300/0x3f00
24 2801 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x500/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2000/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2400/0x3f00
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0
Chain rx_eth0 (2 references)
pkts bytes target prot opt in out source destination
73 11938 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x200/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x300/0x3f00
5 694 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x500/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2000/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2400/0x3f00
6 345 all -- * * 0.0.0.0/0 0.0.0.0/0
Chain rx_wlan0 (2 references)
pkts bytes target prot opt in out source destination
153 28098 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x200/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x300/0x3f00
6 840 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x500/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2000/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match 0x2400/0x3f00
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0
)";
const char kIp6tablesOutput[] = R"(
Chain PREROUTING (policy ACCEPT 22785 packets, 136093545 bytes)
pkts bytes target prot opt in out source destination
18 2196 MARK all -- arcbr0 * ::/0 ::/0 MARK set 0x1
0 0 MARK all -- vmtap+ * ::/0 ::/0 MARK set 0x1
6526 68051766 MARK all -- arc_eth0 * ::/0 ::/0 MARK set 0x1
9 1104 MARK all -- arc_wlan0 * ::/0 ::/0 MARK set 0x1
Chain INPUT (policy ACCEPT 4421 packets, 2461233 bytes)
pkts bytes target prot opt in out source destination
312491 1767147156 rx_eth0 all -- eth0 * ::/0 ::/0
0 0 rx_wlan0 all -- wlan0 * ::/0 ::/0
Chain FORWARD (policy ACCEPT 18194 packets, 133612816 bytes)
pkts bytes target prot opt in out source destination
6511 68041668 tx_eth0 all -- * eth0 ::/0 ::/0
11683 65571148 rx_eth0 all -- eth0 * ::/0 ::/0
Chain OUTPUT (policy ACCEPT 4574 packets, 2900995 bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 22811 packets, 136518827 bytes)
pkts bytes target prot opt in out source destination
202160 1807550291 tx_eth0 all -- * eth0 ::/0 ::/0 owner socket exists
2 96 tx_wlan0 all -- * wlan0 ::/0 ::/0 owner socket exists
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
1366 244427 RETURN all -- * * ::/0 ::/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x200/0x3f00
20 1670 RETURN all -- * * ::/0 ::/0 mark match 0x300/0x3f00
550 138402 RETURN all -- * * ::/0 ::/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x500/0x3f00
5374 876172 RETURN all -- * * ::/0 ::/0 mark match 0x2000/0x3f00
39 2690 RETURN all -- * * ::/0 ::/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2400/0x3f00
4 123 all -- * * ::/0 ::/0
Chain tx_wlan0 (1 references)
pkts bytes target prot opt in out source destination
310 57004 RETURN all -- * * ::/0 ::/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x200/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x300/0x3f00
24 2801 RETURN all -- * * ::/0 ::/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x500/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2000/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2400/0x3f00
0 0 all -- * * ::/0 ::/0
Chain rx_eth0 (2 references)
pkts bytes target prot opt in out source destination
73 11938 RETURN all -- * * ::/0 ::/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x200/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x300/0x3f00
5 694 RETURN all -- * * ::/0 ::/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x500/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2000/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2400/0x3f00
6 345 all -- * * ::/0 ::/0
Chain rx_wlan0 (2 references)
pkts bytes target prot opt in out source destination
153 28098 RETURN all -- * * ::/0 ::/0 mark match 0x100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x200/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x300/0x3f00
6 840 RETURN all -- * * ::/0 ::/0 mark match 0x400/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x500/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2000/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2100/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2200/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2300/0x3f00
0 0 RETURN all -- * * ::/0 ::/0 mark match 0x2400/0x3f00
0 0 all -- * * ::/0 ::/0
)";
bool CompareCounters(std::map<CounterKey, Counter> expected,
std::map<CounterKey, Counter> actual) {
bool success = true;
for (const auto& kv : expected) {
const auto it = actual.find(kv.first);
if (it == actual.end()) {
LOG(ERROR) << "Could not find expected CounterKey=" << kv.first;
success = false;
continue;
}
if (!(it->second == kv.second)) {
LOG(ERROR) << "Unexpected Counter=" << it->second
<< " for CounterKey=" << kv.first << ". Expected instead "
<< kv.second;
success = false;
}
}
for (const auto& kv : actual) {
if (expected.find(kv.first) == expected.end()) {
LOG(ERROR) << "Unexpected entry CounterKey=" << kv.first
<< " Counter=" << kv.second;
success = false;
}
}
return success;
}
class CountersServiceTest : public testing::Test {
protected:
void SetUp() override {
datapath_ = std::make_unique<MockDatapath>();
counters_svc_ = std::make_unique<CountersService>(datapath_.get());
}
// Makes `iptables` and `ip6tables` returning |ipv4_output| and
// |ipv6_output|, respectively. Expects an empty map from GetCounters().
void TestBadIptablesOutput(const std::string& ipv4_output,
const std::string& ipv6_output) {
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv4, "mangle"))
.WillRepeatedly(Return(ipv4_output));
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv6, "mangle"))
.WillRepeatedly(Return(ipv6_output));
auto actual = counters_svc_->GetCounters({});
std::map<CounterKey, Counter> expected;
EXPECT_TRUE(CompareCounters(expected, actual));
}
std::unique_ptr<MockDatapath> datapath_;
std::unique_ptr<CountersService> counters_svc_;
};
TEST_F(CountersServiceTest, OnPhysicalDeviceAdded) {
// The following commands are expected when eth0 comes up.
EXPECT_CALL(*datapath_,
ModifyChain(IpFamily::Dual, "mangle", "-N", "rx_eth0", _))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ModifyChain(IpFamily::Dual, "mangle", "-N", "tx_eth0", _))
.WillOnce(Return(true));
const std::vector<std::vector<std::string>> expected_calls{
{"-A", "INPUT", "-i", "eth0", "-j", "rx_eth0", "-w"},
{"-A", "FORWARD", "-i", "eth0", "-j", "rx_eth0", "-w"},
{"-A", "POSTROUTING", "-o", "eth0", "-j", "tx_eth0", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
"RETURN", "-w"},
{"-A", "tx_eth0", "-w"},
{"-A", "rx_eth0", "-w"},
};
for (const auto& rule : expected_calls) {
EXPECT_CALL(*datapath_, ModifyIptables(IpFamily::Dual, "mangle",
ElementsAreArray(rule), _));
}
counters_svc_->OnPhysicalDeviceAdded("eth0");
}
TEST_F(CountersServiceTest, OnPhysicalDeviceRemoved) {
const std::vector<std::vector<std::string>> expected_calls{
{"-D", "INPUT", "-i", "eth0", "-j", "rx_eth0", "-w"},
{"-D", "FORWARD", "-i", "eth0", "-j", "rx_eth0", "-w"},
{"-D", "POSTROUTING", "-o", "eth0", "-j", "tx_eth0", "-w"},
};
for (const auto& rule : expected_calls) {
EXPECT_CALL(*datapath_, ModifyIptables(IpFamily::Dual, "mangle",
ElementsAreArray(rule), _));
}
counters_svc_->OnPhysicalDeviceRemoved("eth0");
}
TEST_F(CountersServiceTest, OnVpnDeviceAdded) {
// The following commands are expected when tun0 comes up.
EXPECT_CALL(*datapath_,
ModifyChain(IpFamily::Dual, "mangle", "-N", "rx_vpn", _))
.WillOnce(Return(true));
EXPECT_CALL(*datapath_,
ModifyChain(IpFamily::Dual, "mangle", "-N", "tx_vpn", _))
.WillOnce(Return(true));
const std::vector<std::vector<std::string>> expected_calls{
{"-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", "tx_vpn", "-w"},
{"-A", "rx_vpn", "-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(*datapath_, ModifyIptables(IpFamily::Dual, "mangle",
ElementsAreArray(rule), _));
}
counters_svc_->OnVpnDeviceAdded("tun0");
}
TEST_F(CountersServiceTest, OnVpnDeviceRemoved) {
const std::vector<std::vector<std::string>> expected_calls{
{"-D", "FORWARD", "-i", "ppp0", "-j", "rx_vpn", "-w"},
{"-D", "INPUT", "-i", "ppp0", "-j", "rx_vpn", "-w"},
{"-D", "POSTROUTING", "-o", "ppp0", "-j", "tx_vpn", "-w"},
};
for (const auto& rule : expected_calls) {
EXPECT_CALL(*datapath_, ModifyIptables(IpFamily::Dual, "mangle",
ElementsAreArray(rule), _));
}
counters_svc_->OnVpnDeviceRemoved("ppp0");
}
TEST_F(CountersServiceTest, OnSameDeviceAppearAgain) {
// Makes the chain creation commands return false (we already have these
// rules).
EXPECT_CALL(*datapath_, ModifyChain(_, "mangle", "-N", _, _))
.WillRepeatedly(Return(false));
// Only the jump rules should be recreated.
const std::vector<std::vector<std::string>> expected_calls{
{"-A", "FORWARD", "-i", "eth0", "-j", "rx_eth0", "-w"},
{"-A", "INPUT", "-i", "eth0", "-j", "rx_eth0", "-w"},
{"-A", "POSTROUTING", "-o", "eth0", "-j", "tx_eth0", "-w"},
};
for (const auto& rule : expected_calls) {
EXPECT_CALL(*datapath_, ModifyIptables(IpFamily::Dual, "mangle",
ElementsAreArray(rule), _));
}
// No fwmark matching rule should be created.
EXPECT_CALL(*datapath_, ModifyIptables(_, "mangle", Contains("mark"), _))
.Times(0);
counters_svc_->OnPhysicalDeviceAdded("eth0");
}
TEST_F(CountersServiceTest, ChainNameLength) {
// The name of a new chain must be shorter than 29 characters, otherwise
// iptables will reject the request. Uses Each() here for simplicity since no
// other params could be longer than 29 for now.
static constexpr int kMaxChainNameLength = 29;
EXPECT_CALL(*datapath_,
ModifyChain(_, "mangle", _, SizeIs(Lt(kMaxChainNameLength)), _))
.Times(AnyNumber());
static const std::string kLongInterfaceName(IFNAMSIZ, 'a');
counters_svc_->OnPhysicalDeviceAdded(kLongInterfaceName);
}
TEST_F(CountersServiceTest, QueryTrafficCounters) {
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv4, "mangle"))
.WillOnce(Return(kIptablesOutput));
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv6, "mangle"))
.WillOnce(Return(kIp6tablesOutput));
auto actual = counters_svc_->GetCounters({});
// The expected counters for eth0 and wlan0. All values are doubled because
// the same output will be returned for both iptables and ip6tables in the
// tests.
std::map<CounterKey, Counter> expected{
{{"eth0", TrafficCounter::CHROME, TrafficCounter::IPV4},
{11938 /*rx_bytes*/, 73 /*rx_packets*/, 244427 /*tx_bytes*/,
1366 /*tx_packets*/}},
{{"eth0", TrafficCounter::UPDATE_ENGINE, TrafficCounter::IPV4},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 1670 /*tx_bytes*/,
20 /*tx_packets*/}},
{{"eth0", TrafficCounter::SYSTEM, TrafficCounter::IPV4},
{694 /*rx_bytes*/, 5 /*rx_packets*/, 138402 /*tx_bytes*/,
550 /*tx_packets*/}},
{{"eth0", TrafficCounter::ARC, TrafficCounter::IPV4},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 876172 /*tx_bytes*/,
5374 /*tx_packets*/}},
{{"eth0", TrafficCounter::CROSVM, TrafficCounter::IPV4},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 2690 /*tx_bytes*/,
39 /*tx_packets*/}},
{{"eth0", TrafficCounter::UNKNOWN, TrafficCounter::IPV4},
{345 /*rx_bytes*/, 6 /*rx_packets*/, 123 /*tx_bytes*/,
4 /*tx_packets*/}},
{{"wlan0", TrafficCounter::CHROME, TrafficCounter::IPV4},
{28098 /*rx_bytes*/, 153 /*rx_packets*/, 57004 /*tx_bytes*/,
310 /*tx_packets*/}},
{{"wlan0", TrafficCounter::SYSTEM, TrafficCounter::IPV4},
{840 /*rx_bytes*/, 6 /*rx_packets*/, 2801 /*tx_bytes*/,
24 /*tx_packets*/}},
{{"eth0", TrafficCounter::CHROME, TrafficCounter::IPV6},
{11938 /*rx_bytes*/, 73 /*rx_packets*/, 244427 /*tx_bytes*/,
1366 /*tx_packets*/}},
{{"eth0", TrafficCounter::UPDATE_ENGINE, TrafficCounter::IPV6},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 1670 /*tx_bytes*/,
20 /*tx_packets*/}},
{{"eth0", TrafficCounter::SYSTEM, TrafficCounter::IPV6},
{694 /*rx_bytes*/, 5 /*rx_packets*/, 138402 /*tx_bytes*/,
550 /*tx_packets*/}},
{{"eth0", TrafficCounter::ARC, TrafficCounter::IPV6},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 876172 /*tx_bytes*/,
5374 /*tx_packets*/}},
{{"eth0", TrafficCounter::CROSVM, TrafficCounter::IPV6},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 2690 /*tx_bytes*/,
39 /*tx_packets*/}},
{{"eth0", TrafficCounter::UNKNOWN, TrafficCounter::IPV6},
{345 /*rx_bytes*/, 6 /*rx_packets*/, 123 /*tx_bytes*/,
4 /*tx_packets*/}},
{{"wlan0", TrafficCounter::CHROME, TrafficCounter::IPV6},
{28098 /*rx_bytes*/, 153 /*rx_packets*/, 57004 /*tx_bytes*/,
310 /*tx_packets*/}},
{{"wlan0", TrafficCounter::SYSTEM, TrafficCounter::IPV6},
{840 /*rx_bytes*/, 6 /*rx_packets*/, 2801 /*tx_bytes*/,
24 /*tx_packets*/}},
};
EXPECT_TRUE(CompareCounters(expected, actual));
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithFilter) {
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv4, "mangle"))
.WillOnce(Return(kIptablesOutput));
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv6, "mangle"))
.WillOnce(Return(kIp6tablesOutput));
// Only counters for eth0 should be returned. eth1 should be ignored.
auto actual = counters_svc_->GetCounters({"eth0", "eth1"});
// The expected counters for eth0. All values are doubled because
// the same output will be returned for both iptables and ip6tables in the
// tests.
std::map<CounterKey, Counter> expected{
{{"eth0", TrafficCounter::CHROME, TrafficCounter::IPV4},
{11938 /*rx_bytes*/, 73 /*rx_packets*/, 244427 /*tx_bytes*/,
1366 /*tx_packets*/}},
{{"eth0", TrafficCounter::UPDATE_ENGINE, TrafficCounter::IPV4},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 1670 /*tx_bytes*/,
20 /*tx_packets*/}},
{{"eth0", TrafficCounter::SYSTEM, TrafficCounter::IPV4},
{694 /*rx_bytes*/, 5 /*rx_packets*/, 138402 /*tx_bytes*/,
550 /*tx_packets*/}},
{{"eth0", TrafficCounter::ARC, TrafficCounter::IPV4},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 876172 /*tx_bytes*/,
5374 /*tx_packets*/}},
{{"eth0", TrafficCounter::CROSVM, TrafficCounter::IPV4},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 2690 /*tx_bytes*/,
39 /*tx_packets*/}},
{{"eth0", TrafficCounter::UNKNOWN, TrafficCounter::IPV4},
{345 /*rx_bytes*/, 6 /*rx_packets*/, 123 /*tx_bytes*/,
4 /*tx_packets*/}},
{{"eth0", TrafficCounter::CHROME, TrafficCounter::IPV6},
{11938 /*rx_bytes*/, 73 /*rx_packets*/, 244427 /*tx_bytes*/,
1366 /*tx_packets*/}},
{{"eth0", TrafficCounter::UPDATE_ENGINE, TrafficCounter::IPV6},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 1670 /*tx_bytes*/,
20 /*tx_packets*/}},
{{"eth0", TrafficCounter::SYSTEM, TrafficCounter::IPV6},
{694 /*rx_bytes*/, 5 /*rx_packets*/, 138402 /*tx_bytes*/,
550 /*tx_packets*/}},
{{"eth0", TrafficCounter::ARC, TrafficCounter::IPV6},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 876172 /*tx_bytes*/,
5374 /*tx_packets*/}},
{{"eth0", TrafficCounter::CROSVM, TrafficCounter::IPV6},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 2690 /*tx_bytes*/,
39 /*tx_packets*/}},
{{"eth0", TrafficCounter::UNKNOWN, TrafficCounter::IPV6},
{345 /*rx_bytes*/, 6 /*rx_packets*/, 123 /*tx_bytes*/,
4 /*tx_packets*/}},
};
EXPECT_TRUE(CompareCounters(expected, actual));
}
TEST_F(CountersServiceTest, QueryTraffic_UnknownTrafficOnly) {
const std::string unknown_ipv4_traffic_only = R"(
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
6511 68041668 all -- * * 0.0.0.0/0 0.0.0.0/0
)";
const std::string unknown_ipv6_traffic_only = R"(
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
211 13456 all -- any any ::/0 ::/0
)";
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv4, "mangle"))
.WillOnce(Return(unknown_ipv4_traffic_only));
EXPECT_CALL(*datapath_, DumpIptables(IpFamily::IPv6, "mangle"))
.WillOnce(Return(unknown_ipv6_traffic_only));
auto actual = counters_svc_->GetCounters({});
std::map<CounterKey, Counter> expected{
{{"eth0", TrafficCounter::UNKNOWN, TrafficCounter::IPV4},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 68041668 /*tx_bytes*/,
6511 /*tx_packets*/}},
{{"eth0", TrafficCounter::UNKNOWN, TrafficCounter::IPV6},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 13456 /*tx_bytes*/,
211 /*tx_packets*/}},
};
EXPECT_TRUE(CompareCounters(expected, actual));
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv4Output) {
TestBadIptablesOutput("", kIp6tablesOutput);
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv6Output) {
TestBadIptablesOutput(kIptablesOutput, "");
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainName) {
const std::string kBadOutput = R"(
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
6511 68041668 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain tx_wlan0 (1 references)
)";
TestBadIptablesOutput(kBadOutput, kIp6tablesOutput);
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainNameAndHeader) {
const std::string kBadOutput = R"(
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
6511 68041668 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain tx_wlan0 (1 references)
pkts bytes target prot opt in out source destination
)";
TestBadIptablesOutput(kBadOutput, kIp6tablesOutput);
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithNotFinishedCountersLine) {
const std::string kBadOutput = R"(
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
6511 68041668 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0
Chain tx_wlan0 (1 references)
pkts bytes target prot opt in out source destination pkts bytes target prot opt in out source destination
0 )";
TestBadIptablesOutput(kBadOutput, kIp6tablesOutput);
}
} // namespace
} // namespace patchpanel