blob: 795d0f1a133043bfb50a04ea5c41197d4ad5555d [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 <chromeos/dbus/service_constants.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "patchpanel/mock_firewall.h"
namespace patchpanel {
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::ContainerEq;
using ::testing::Contains;
using ::testing::DoAll;
using ::testing::Each;
using ::testing::ElementsAreArray;
using ::testing::Lt;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SizeIs;
using Counter = CountersService::Counter;
using SourceDevice = CountersService::SourceDevice;
// The following two functions should be put outside the anounymous 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;
}
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`. This output contains all the accounting
// chains/rules for eth0 and wlan0.
// TODO(jiejiang): presubmit checker is complaining about the line length for
// this (and the other raw strings in this file). Find a way to make it happy.
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 any anywhere anywhere MARK set 0x1
0 0 MARK all -- vmtap+ any anywhere anywhere MARK set 0x1
6526 68051766 MARK all -- arc_eth0 any anywhere anywhere MARK set 0x1
9 1104 MARK all -- arc_wlan0 any anywhere anywhere 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 any anywhere anywhere
0 0 rx_wlan0 all -- wlan0 any anywhere anywhere
Chain FORWARD (policy ACCEPT 18194 packets, 133612816 bytes)
pkts bytes target prot opt in out source destination
6511 68041668 tx_eth0 all -- any eth0 anywhere anywhere
11683 65571148 rx_eth0 all -- eth0 any anywhere anywhere
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 -- any eth0 anywhere anywhere owner socket exists
2 96 tx_wlan0 all -- any wlan0 anywhere anywhere owner socket exists
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
1366 244427 RETURN all -- any any anywhere anywhere mark match 0x100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x200/0x3f00
20 1670 RETURN all -- any any anywhere anywhere mark match 0x300/0x3f00
550 138402 RETURN all -- any any anywhere anywhere mark match 0x400/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x500/0x3f00
5374 876172 RETURN all -- any any anywhere anywhere mark match 0x2000/0x3f00
39 2690 RETURN all -- any any anywhere anywhere mark match 0x2100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2200/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2300/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2400/0x3f00
4 123 all -- any any anywhere anywhere
Chain tx_wlan0 (1 references)
pkts bytes target prot opt in out source destination
310 57004 RETURN all -- any any anywhere anywhere mark match 0x100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x200/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x300/0x3f00
24 2801 RETURN all -- any any anywhere anywhere mark match 0x400/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x500/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2000/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2200/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2300/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2400/0x3f00
0 0 all -- any any anywhere anywhere
Chain rx_eth0 (2 references)
pkts bytes target prot opt in out source destination
73 11938 RETURN all -- any any anywhere anywhere mark match 0x100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x200/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x300/0x3f00
5 694 RETURN all -- any any anywhere anywhere mark match 0x400/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x500/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2000/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2200/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2300/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2400/0x3f00
6 345 all -- any any anywhere anywhere
Chain rx_wlan0 (2 references)
pkts bytes target prot opt in out source destination
153 28098 RETURN all -- any any anywhere anywhere mark match 0x100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x200/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x300/0x3f00
6 840 RETURN all -- any any anywhere anywhere mark match 0x400/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x500/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2000/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2100/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2200/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2300/0x3f00
0 0 RETURN all -- any any anywhere anywhere mark match 0x2400/0x3f00
0 0 all -- any any anywhere anywhere
)";
class MockProcessRunner : public MinijailedProcessRunner {
public:
MockProcessRunner() = default;
~MockProcessRunner() = default;
MOCK_METHOD(int,
iptables,
(const std::string& table,
const std::vector<std::string>& argv,
bool log_failures,
std::string* output),
(override));
MOCK_METHOD(int,
ip6tables,
(const std::string& table,
const std::vector<std::string>& argv,
bool log_failures,
std::string* output),
(override));
};
class CountersServiceTest : public testing::Test {
protected:
void SetUp() override {
datapath_ = std::make_unique<Datapath>(&runner_, &firewall_);
counters_svc_ =
std::make_unique<CountersService>(datapath_.get(), &runner_);
}
// 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(runner_, iptables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(ipv4_output), Return(0)));
EXPECT_CALL(runner_, ip6tables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(ipv6_output), Return(0)));
auto actual = counters_svc_->GetCounters({});
std::map<SourceDevice, Counter> expected;
EXPECT_THAT(actual, ContainerEq(expected));
}
MockProcessRunner runner_;
MockFirewall firewall_;
std::unique_ptr<Datapath> datapath_;
std::unique_ptr<CountersService> counters_svc_;
};
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"},
{"-A", "tx_vpn", "-w"},
{"-A", "rx_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({});
}
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"},
{"-A", "tx_vpn", "-w"},
{"-A", "rx_vpn", "-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"},
{"-A", "tx_wlan0", "-w"},
{"-A", "rx_wlan0", "-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"},
{"-N", "tx_eth0", "-w"},
{"-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(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
}
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", "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(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
}
counters_svc_->Init({});
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(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
EXPECT_CALL(runner_, ip6tables("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(runner_, iptables(_, Contains("-N"), _, _))
.WillRepeatedly(Return(1));
EXPECT_CALL(runner_, ip6tables(_, Contains("-N"), _, _))
.WillRepeatedly(Return(1));
// Creating chains commands are expected but no more creating rules command
// (with "-I" or "-A") should come.
EXPECT_CALL(runner_, iptables(_, Contains("-A"), _, _)).Times(0);
EXPECT_CALL(runner_, ip6tables(_, Contains("-A"), _, _)).Times(0);
EXPECT_CALL(runner_, iptables(_, Contains("-I"), _, _)).Times(0);
EXPECT_CALL(runner_, ip6tables(_, Contains("-I"), _, _)).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(runner_, iptables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
.Times(AnyNumber());
EXPECT_CALL(runner_,
ip6tables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
.Times(AnyNumber());
static const std::string kLongInterfaceName(IFNAMSIZ, 'a');
counters_svc_->OnPhysicalDeviceAdded(kLongInterfaceName);
}
TEST_F(CountersServiceTest, QueryTrafficCounters) {
EXPECT_CALL(runner_, iptables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
EXPECT_CALL(runner_, ip6tables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
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<SourceDevice, Counter> expected{
{{TrafficCounter::CHROME, "eth0"},
{23876 /*rx_bytes*/, 146 /*rx_packets*/, 488854 /*tx_bytes*/,
2732 /*tx_packets*/}},
{{TrafficCounter::UPDATE_ENGINE, "eth0"},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 3340 /*tx_bytes*/,
40 /*tx_packets*/}},
{{TrafficCounter::SYSTEM, "eth0"},
{1388 /*rx_bytes*/, 10 /*rx_packets*/, 276804 /*tx_bytes*/,
1100 /*tx_packets*/}},
{{TrafficCounter::ARC, "eth0"},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 1752344 /*tx_bytes*/,
10748 /*tx_packets*/}},
{{TrafficCounter::CROSVM, "eth0"},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 5380 /*tx_bytes*/,
78 /*tx_packets*/}},
{{TrafficCounter::UNKNOWN, "eth0"},
{690 /*rx_bytes*/, 12 /*rx_packets*/, 246 /*tx_bytes*/,
8 /*tx_packets*/}},
{{TrafficCounter::CHROME, "wlan0"},
{56196 /*rx_bytes*/, 306 /*rx_packets*/, 114008 /*tx_bytes*/,
620 /*tx_packets*/}},
{{TrafficCounter::SYSTEM, "wlan0"},
{1680 /*rx_bytes*/, 12 /*rx_packets*/, 5602 /*tx_bytes*/,
48 /*tx_packets*/}},
};
EXPECT_THAT(actual, ContainerEq(expected));
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithFilter) {
EXPECT_CALL(runner_, iptables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
EXPECT_CALL(runner_, ip6tables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
// 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<SourceDevice, Counter> expected{
{{TrafficCounter::CHROME, "eth0"},
{23876 /*rx_bytes*/, 146 /*rx_packets*/, 488854 /*tx_bytes*/,
2732 /*tx_packets*/}},
{{TrafficCounter::UPDATE_ENGINE, "eth0"},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 3340 /*tx_bytes*/,
40 /*tx_packets*/}},
{{TrafficCounter::SYSTEM, "eth0"},
{1388 /*rx_bytes*/, 10 /*rx_packets*/, 276804 /*tx_bytes*/,
1100 /*tx_packets*/}},
{{TrafficCounter::ARC, "eth0"},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 1752344 /*tx_bytes*/,
10748 /*tx_packets*/}},
{{TrafficCounter::CROSVM, "eth0"},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 5380 /*tx_bytes*/,
78 /*tx_packets*/}},
{{TrafficCounter::UNKNOWN, "eth0"},
{690 /*rx_bytes*/, 12 /*rx_packets*/, 246 /*tx_bytes*/,
8 /*tx_packets*/}},
};
EXPECT_THAT(actual, ContainerEq(expected));
}
TEST_F(CountersServiceTest, QueryTraffic_UnknownTrafficOnly) {
const std::string unkwown_traffic_only = R"(
Chain tx_eth0 (1 references)
pkts bytes target prot opt in out source destination
6511 68041668 all -- any any anywhere anywhere
)";
EXPECT_CALL(runner_, iptables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(unkwown_traffic_only), Return(0)));
EXPECT_CALL(runner_, ip6tables(_, _, _, _))
.WillRepeatedly(DoAll(SetArgPointee<3>(unkwown_traffic_only), Return(0)));
auto actual = counters_svc_->GetCounters({});
std::map<SourceDevice, Counter> expected{
{{TrafficCounter::UNKNOWN, "eth0"},
{0 /*rx_bytes*/, 0 /*rx_packets*/, 136083336 /*tx_bytes*/,
13022 /*tx_packets*/}},
};
EXPECT_THAT(actual, ContainerEq(expected));
}
TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv4Output) {
TestBadIptablesOutput("", kIptablesOutput);
}
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 -- any any anywhere anywhere
Chain tx_wlan0 (1 references)
)";
TestBadIptablesOutput(kBadOutput, kIptablesOutput);
}
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 -- any any anywhere anywhere
Chain tx_wlan0 (1 references)
pkts bytes target prot opt in out source destination
)";
TestBadIptablesOutput(kBadOutput, kIptablesOutput);
}
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 -- any any anywhere anywhere
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, kIptablesOutput);
}
} // namespace
} // namespace patchpanel