blob: 6e6bcc4bdcce8e460a33157d4a00acd9a0d290cf [file] [log] [blame]
Jie Jiangcf749152020-07-09 22:20:58 +09001// Copyright 2020 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "patchpanel/counters_service.h"
6
7#include <memory>
Jie Jiang8fd0ace2020-09-01 14:51:57 +09008#include <net/if.h>
Jie Jiangcf749152020-07-09 22:20:58 +09009#include <string>
10#include <vector>
11
12#include <chromeos/dbus/service_constants.h>
13#include <gmock/gmock.h>
14#include <gtest/gtest.h>
15
16#include "patchpanel/fake_shill_client.h"
Hugo Benichicd27f4e2020-11-19 18:32:23 +090017#include "patchpanel/mock_firewall.h"
Jie Jiangcf749152020-07-09 22:20:58 +090018
19namespace patchpanel {
Jie Jiangcf749152020-07-09 22:20:58 +090020
Jie Jianged0b1cc2020-07-10 15:55:33 +090021using ::testing::ContainerEq;
Jie Jiangcf749152020-07-09 22:20:58 +090022using ::testing::Contains;
Jie Jianged0b1cc2020-07-10 15:55:33 +090023using ::testing::DoAll;
Jie Jiang8fd0ace2020-09-01 14:51:57 +090024using ::testing::Each;
Jie Jiangcf749152020-07-09 22:20:58 +090025using ::testing::ElementsAreArray;
Jie Jiang8fd0ace2020-09-01 14:51:57 +090026using ::testing::Lt;
Jie Jianged0b1cc2020-07-10 15:55:33 +090027using ::testing::Return;
28using ::testing::SetArgPointee;
Jie Jiang8fd0ace2020-09-01 14:51:57 +090029using ::testing::SizeIs;
Jie Jianged0b1cc2020-07-10 15:55:33 +090030
31using Counter = CountersService::Counter;
32using SourceDevice = CountersService::SourceDevice;
33
34// The following two functions should be put outside the anounymous namespace
35// otherwise they could not be found in the tests.
36std::ostream& operator<<(std::ostream& os, const Counter& counter) {
37 os << "rx_bytes:" << counter.rx_bytes << ", rx_packets:" << counter.rx_packets
38 << ", tx_bytes:" << counter.tx_bytes
39 << ", tx_packets:" << counter.tx_packets;
40 return os;
41}
42
43bool operator==(const CountersService::Counter lhs,
44 const CountersService::Counter rhs) {
45 return lhs.rx_bytes == rhs.rx_bytes && lhs.rx_packets == rhs.rx_packets &&
46 lhs.tx_bytes == rhs.tx_bytes && lhs.tx_packets == rhs.tx_packets;
47}
48
49namespace {
50// The following string is copied from the real output of iptables v1.6.2 by
51// `iptables -t mangle -L -x -v`. This output contains all the accounting
52// chains/rules for eth0 and wlan0.
53// TODO(jiejiang): presubmit checker is complaining about the line length for
54// this (and the other raw strings in this file). Find a way to make it happy.
55const char kIptablesOutput[] = R"(
56Chain PREROUTING (policy ACCEPT 22785 packets, 136093545 bytes)
57 pkts bytes target prot opt in out source destination
58 18 2196 MARK all -- arcbr0 any anywhere anywhere MARK set 0x1
59 0 0 MARK all -- vmtap+ any anywhere anywhere MARK set 0x1
60 6526 68051766 MARK all -- arc_eth0 any anywhere anywhere MARK set 0x1
61 9 1104 MARK all -- arc_wlan0 any anywhere anywhere MARK set 0x1
62
63Chain INPUT (policy ACCEPT 4421 packets, 2461233 bytes)
64 pkts bytes target prot opt in out source destination
Hugo Benichi33af8f72020-11-20 10:08:47 +090065 312491 1767147156 rx_eth0 all -- eth0 any anywhere anywhere
66 0 0 rx_wlan0 all -- wlan0 any anywhere anywhere
Jie Jianged0b1cc2020-07-10 15:55:33 +090067
68Chain FORWARD (policy ACCEPT 18194 packets, 133612816 bytes)
69 pkts bytes target prot opt in out source destination
Hugo Benichi33af8f72020-11-20 10:08:47 +090070 6511 68041668 tx_eth0 all -- any eth0 anywhere anywhere
71 11683 65571148 rx_eth0 all -- eth0 any anywhere anywhere
Jie Jianged0b1cc2020-07-10 15:55:33 +090072
73Chain OUTPUT (policy ACCEPT 4574 packets, 2900995 bytes)
74 pkts bytes target prot opt in out source destination
75
76Chain POSTROUTING (policy ACCEPT 22811 packets, 136518827 bytes)
77 pkts bytes target prot opt in out source destination
Hugo Benichi33af8f72020-11-20 10:08:47 +090078 202160 1807550291 tx_eth0 all -- any eth0 anywhere anywhere owner socket exists
79 2 96 tx_wlan0 all -- any wlan0 anywhere anywhere owner socket exists
Jie Jianged0b1cc2020-07-10 15:55:33 +090080
Hugo Benichi33af8f72020-11-20 10:08:47 +090081Chain tx_eth0 (1 references)
Jie Jianged0b1cc2020-07-10 15:55:33 +090082 pkts bytes target prot opt in out source destination
Hugo Benichi33af8f72020-11-20 10:08:47 +090083 208671 1875591959 all -- any any anywhere anywhere
Jie Jianged0b1cc2020-07-10 15:55:33 +090084
Hugo Benichi33af8f72020-11-20 10:08:47 +090085Chain tx_wlan0 (1 references)
Jie Jianged0b1cc2020-07-10 15:55:33 +090086 pkts bytes target prot opt in out source destination
87 2 96 all -- any any anywhere anywhere
88
Hugo Benichi33af8f72020-11-20 10:08:47 +090089Chain rx_eth0 (2 references)
Jie Jianged0b1cc2020-07-10 15:55:33 +090090 pkts bytes target prot opt in out source destination
Hugo Benichi33af8f72020-11-20 10:08:47 +090091 324174 1832718304 all -- any any anywhere anywhere
Jie Jianged0b1cc2020-07-10 15:55:33 +090092
Hugo Benichi33af8f72020-11-20 10:08:47 +090093Chain rx_wlan0 (2 references)
Jie Jianged0b1cc2020-07-10 15:55:33 +090094 pkts bytes target prot opt in out source destination
95 0 0 all -- any any anywhere anywhere
96)";
97
98// The expected counters for the above output. "* 2" because the same string
99// will be returned for both iptables and ip6tables in the tests.
100const Counter kCounter_eth0{(65571148 + 1767147156ULL) * 2 /*rx_bytes*/,
101 (11683 + 312491) * 2 /*rx_packets*/,
102 (68041668 + 1807550291ULL) * 2 /*tx_bytes*/,
103 (6511 + 202160) * 2 /*tx_packets*/};
104const Counter kCounter_wlan0{0, 0, 96 * 2, 2 * 2};
Jie Jiangcf749152020-07-09 22:20:58 +0900105
106class MockProcessRunner : public MinijailedProcessRunner {
107 public:
108 MockProcessRunner() = default;
109 ~MockProcessRunner() = default;
110
111 MOCK_METHOD(int,
112 iptables,
113 (const std::string& table,
114 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900115 bool log_failures,
116 std::string* output),
Jie Jiangcf749152020-07-09 22:20:58 +0900117 (override));
118 MOCK_METHOD(int,
119 ip6tables,
120 (const std::string& table,
121 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900122 bool log_failures,
123 std::string* output),
Jie Jiangcf749152020-07-09 22:20:58 +0900124 (override));
125};
126
127class CountersServiceTest : public testing::Test {
128 protected:
129 void SetUp() override {
130 fake_shill_client_ = shill_helper_.FakeClient();
Hugo Benichicd27f4e2020-11-19 18:32:23 +0900131 datapath_ = std::make_unique<Datapath>(&runner_, &firewall_);
132 counters_svc_ = std::make_unique<CountersService>(
133 fake_shill_client_.get(), datapath_.get(), &runner_);
Jie Jiangcf749152020-07-09 22:20:58 +0900134 }
135
Jie Jianged0b1cc2020-07-10 15:55:33 +0900136 // Makes `iptables` returning a bad |output|. Expects an empty map from
137 // GetCounters().
138 void TestBadIptablesOutput(const std::string& output) {
139 EXPECT_CALL(runner_, iptables(_, _, _, _))
140 .WillRepeatedly(DoAll(SetArgPointee<3>(output), Return(0)));
141 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
142 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
143
144 auto actual = counters_svc_->GetCounters({});
145 std::map<SourceDevice, Counter> expected;
146
147 EXPECT_THAT(actual, ContainerEq(expected));
148 }
149
150 // Makes `ip6tables` returning a bad |output|. Expects an empty map from
151 // GetCounters().
152 void TestBadIp6tablesOutput(const std::string& output) {
153 EXPECT_CALL(runner_, iptables(_, _, _, _))
154 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
155 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
156 .WillRepeatedly(DoAll(SetArgPointee<3>(output), Return(0)));
157
158 auto actual = counters_svc_->GetCounters({});
159 std::map<SourceDevice, Counter> expected;
160
161 EXPECT_THAT(actual, ContainerEq(expected));
162 }
163
Jie Jiangcf749152020-07-09 22:20:58 +0900164 FakeShillClientHelper shill_helper_;
165 MockProcessRunner runner_;
Hugo Benichicd27f4e2020-11-19 18:32:23 +0900166 MockFirewall firewall_;
Jie Jiangcf749152020-07-09 22:20:58 +0900167 std::unique_ptr<FakeShillClient> fake_shill_client_;
Hugo Benichicd27f4e2020-11-19 18:32:23 +0900168 std::unique_ptr<Datapath> datapath_;
Jie Jiangcf749152020-07-09 22:20:58 +0900169 std::unique_ptr<CountersService> counters_svc_;
170};
171
172TEST_F(CountersServiceTest, OnNewDevice) {
Jie Jiangcf749152020-07-09 22:20:58 +0900173 // The following commands are expected when eth0 comes up.
174 const std::vector<std::vector<std::string>> expected_calls{
Hugo Benichi33af8f72020-11-20 10:08:47 +0900175 {"-N", "rx_eth0", "-w"},
176 {"-A", "INPUT", "-i", "eth0", "-j", "rx_eth0", "-w"},
177 {"-A", "FORWARD", "-i", "eth0", "-j", "rx_eth0", "-w"},
178 {"-A", "rx_eth0", "-w"},
Jie Jiangcf749152020-07-09 22:20:58 +0900179
Hugo Benichi33af8f72020-11-20 10:08:47 +0900180 {"-N", "tx_eth0", "-w"},
181 {"-A", "POSTROUTING", "-o", "eth0", "-j", "tx_eth0", "-w"},
182 {"-A", "tx_eth0", "-w"},
Jie Jiangcf749152020-07-09 22:20:58 +0900183 };
184
185 for (const auto& rule : expected_calls) {
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900186 EXPECT_CALL(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
187 EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
Jie Jiangcf749152020-07-09 22:20:58 +0900188 }
189
190 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
191 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
192 brillo::Any(devices));
193}
194
195TEST_F(CountersServiceTest, OnSameDeviceAppearAgain) {
Hugo Benichiddf00842020-11-20 10:24:08 +0900196 // Makes the chain creation commands return false (we already have these
197 // rules).
198 EXPECT_CALL(runner_, iptables(_, Contains("-N"), _, _))
199 .WillRepeatedly(Return(1));
200 EXPECT_CALL(runner_, ip6tables(_, Contains("-N"), _, _))
201 .WillRepeatedly(Return(1));
Jie Jiangcf749152020-07-09 22:20:58 +0900202
203 // Creating chains commands are expected but no more creating rules command
204 // (with "-I" or "-A") should come.
Hugo Benichiddf00842020-11-20 10:24:08 +0900205 EXPECT_CALL(runner_, iptables(_, Contains("-A"), _, _)).Times(0);
206 EXPECT_CALL(runner_, ip6tables(_, Contains("-A"), _, _)).Times(0);
207 EXPECT_CALL(runner_, iptables(_, Contains("-I"), _, _)).Times(0);
208 EXPECT_CALL(runner_, ip6tables(_, Contains("-I"), _, _)).Times(0);
Jie Jiangcf749152020-07-09 22:20:58 +0900209
210 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
211 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
212 brillo::Any(devices));
213}
214
Jie Jiang8fd0ace2020-09-01 14:51:57 +0900215TEST_F(CountersServiceTest, ChainNameLength) {
Jie Jiang8fd0ace2020-09-01 14:51:57 +0900216 // The name of a new chain must be shorter than 29 characters, otherwise
217 // iptables will reject the request. Uses Each() here for simplicity since no
218 // other params could be longer than 29 for now.
219 static constexpr int kMaxChainNameLength = 29;
220 EXPECT_CALL(runner_, iptables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
221 .Times(AnyNumber());
222 EXPECT_CALL(runner_,
223 ip6tables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
224 .Times(AnyNumber());
225
226 static const std::string kLongInterfaceName(IFNAMSIZ, 'a');
227 std::vector<dbus::ObjectPath> devices = {
228 dbus::ObjectPath("/device/" + kLongInterfaceName)};
229 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
230 brillo::Any(devices));
231}
232
Jie Jianged0b1cc2020-07-10 15:55:33 +0900233TEST_F(CountersServiceTest, QueryTrafficCounters) {
234 EXPECT_CALL(runner_, iptables(_, _, _, _))
235 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
236 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
237 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
238
239 auto actual = counters_svc_->GetCounters({});
240
241 std::map<SourceDevice, Counter> expected{
242 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
243 {{TrafficCounter::UNKNOWN, "wlan0"}, kCounter_wlan0},
244 };
245
246 EXPECT_THAT(actual, ContainerEq(expected));
247}
248
249TEST_F(CountersServiceTest, QueryTrafficCountersWithFilter) {
250 EXPECT_CALL(runner_, iptables(_, _, _, _))
251 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
252 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
253 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
254
255 // Only counters for eth0 should be returned. eth1 should be ignored.
256 auto actual = counters_svc_->GetCounters({"eth0", "eth1"});
257
258 std::map<SourceDevice, Counter> expected{
259 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
260 };
261
262 EXPECT_THAT(actual, ContainerEq(expected));
263}
264
265TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv4Output) {
266 const std::string kEmptyOutput = "";
267 TestBadIptablesOutput(kEmptyOutput);
268}
269
270TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv6Output) {
271 const std::string kEmptyOutput = "";
272 TestBadIp6tablesOutput(kEmptyOutput);
273}
274
275TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainName) {
276 const std::string kBadOutput = R"(
277Chain tx_fwd_eth0 (1 references)
278 pkts bytes target prot opt in out source destination
279 6511 68041668 all -- any any anywhere anywhere
280
281Chain tx_fwd_wlan0 (1 references)
282)";
283 TestBadIptablesOutput(kBadOutput);
284}
285
286TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainNameAndHeader) {
287 const std::string kBadOutput = R"(
288Chain tx_fwd_eth0 (1 references)
289 pkts bytes target prot opt in out source destination
290 6511 68041668 all -- any any anywhere anywhere
291
292Chain tx_fwd_wlan0 (1 references)
293 pkts bytes target prot opt in out source destination
294)";
295 TestBadIptablesOutput(kBadOutput);
296}
297
298TEST_F(CountersServiceTest, QueryTrafficCountersWithNotFinishedCountersLine) {
299 const std::string kBadOutput = R"(
300Chain tx_fwd_eth0 (1 references)
301 pkts bytes target prot opt in out source destination
302 6511 68041668 all -- any any anywhere anywhere
303
304Chain tx_fwd_wlan0 (1 references)
305 pkts bytes target prot opt in out source destination pkts bytes target prot opt in out source destination
306 0 )";
307 TestBadIptablesOutput(kBadOutput);
308}
309
Jie Jiangcf749152020-07-09 22:20:58 +0900310} // namespace
311} // namespace patchpanel