blob: 2e6f9041129539b721a056db142d49400b0ea59f [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>
8#include <string>
9#include <vector>
10
11#include <chromeos/dbus/service_constants.h>
12#include <gmock/gmock.h>
13#include <gtest/gtest.h>
14
15#include "patchpanel/fake_shill_client.h"
16
17namespace patchpanel {
Jie Jiangcf749152020-07-09 22:20:58 +090018
Jie Jianged0b1cc2020-07-10 15:55:33 +090019using ::testing::ContainerEq;
Jie Jiangcf749152020-07-09 22:20:58 +090020using ::testing::Contains;
Jie Jianged0b1cc2020-07-10 15:55:33 +090021using ::testing::DoAll;
Jie Jiangcf749152020-07-09 22:20:58 +090022using ::testing::ElementsAreArray;
Jie Jianged0b1cc2020-07-10 15:55:33 +090023using ::testing::Return;
24using ::testing::SetArgPointee;
25
26using Counter = CountersService::Counter;
27using SourceDevice = CountersService::SourceDevice;
28
29// The following two functions should be put outside the anounymous namespace
30// otherwise they could not be found in the tests.
31std::ostream& operator<<(std::ostream& os, const Counter& counter) {
32 os << "rx_bytes:" << counter.rx_bytes << ", rx_packets:" << counter.rx_packets
33 << ", tx_bytes:" << counter.tx_bytes
34 << ", tx_packets:" << counter.tx_packets;
35 return os;
36}
37
38bool operator==(const CountersService::Counter lhs,
39 const CountersService::Counter rhs) {
40 return lhs.rx_bytes == rhs.rx_bytes && lhs.rx_packets == rhs.rx_packets &&
41 lhs.tx_bytes == rhs.tx_bytes && lhs.tx_packets == rhs.tx_packets;
42}
43
44namespace {
45// The following string is copied from the real output of iptables v1.6.2 by
46// `iptables -t mangle -L -x -v`. This output contains all the accounting
47// chains/rules for eth0 and wlan0.
48// TODO(jiejiang): presubmit checker is complaining about the line length for
49// this (and the other raw strings in this file). Find a way to make it happy.
50const char kIptablesOutput[] = R"(
51Chain PREROUTING (policy ACCEPT 22785 packets, 136093545 bytes)
52 pkts bytes target prot opt in out source destination
53 18 2196 MARK all -- arcbr0 any anywhere anywhere MARK set 0x1
54 0 0 MARK all -- vmtap+ any anywhere anywhere MARK set 0x1
55 6526 68051766 MARK all -- arc_eth0 any anywhere anywhere MARK set 0x1
56 9 1104 MARK all -- arc_wlan0 any anywhere anywhere MARK set 0x1
57
58Chain INPUT (policy ACCEPT 4421 packets, 2461233 bytes)
59 pkts bytes target prot opt in out source destination
60 312491 1767147156 rx_input_eth0 all -- eth0 any anywhere anywhere
61 0 0 rx_input_wlan0 all -- wlan0 any anywhere anywhere
62
63Chain FORWARD (policy ACCEPT 18194 packets, 133612816 bytes)
64 pkts bytes target prot opt in out source destination
65 6511 68041668 tx_fwd_eth0 all -- any eth0 anywhere anywhere
66 11683 65571148 rx_fwd_eth0 all -- eth0 any anywhere anywhere
67 0 0 tx_fwd_wlan0 all -- any wlan0 anywhere anywhere
68 0 0 rx_fwd_wlan0 all -- wlan0 any anywhere anywhere
69
70Chain OUTPUT (policy ACCEPT 4574 packets, 2900995 bytes)
71 pkts bytes target prot opt in out source destination
72
73Chain POSTROUTING (policy ACCEPT 22811 packets, 136518827 bytes)
74 pkts bytes target prot opt in out source destination
75 202160 1807550291 tx_postrt_eth0 all -- any eth0 anywhere anywhere owner socket exists
76 2 96 tx_postrt_wlan0 all -- any wlan0 anywhere anywhere owner socket exists
77
78Chain tx_fwd_eth0 (1 references)
79 pkts bytes target prot opt in out source destination
80 6511 68041668 all -- any any anywhere anywhere
81
82Chain tx_fwd_wlan0 (1 references)
83 pkts bytes target prot opt in out source destination
84 0 0 all -- any any anywhere anywhere
85
86Chain tx_postrt_eth0 (1 references)
87 pkts bytes target prot opt in out source destination
88 202160 1807550291 all -- any any anywhere anywhere
89
90Chain tx_postrt_wlan0 (1 references)
91 pkts bytes target prot opt in out source destination
92 2 96 all -- any any anywhere anywhere
93
94Chain rx_fwd_eth0 (1 references)
95 pkts bytes target prot opt in out source destination
96 11683 65571148 all -- any any anywhere anywhere
97
98Chain rx_fwd_wlan0 (1 references)
99 pkts bytes target prot opt in out source destination
100 0 0 all -- any any anywhere anywhere
101
102Chain rx_input_eth0 (1 references)
103 pkts bytes target prot opt in out source destination
104 312491 1767147156 all -- any any anywhere anywhere
105
106Chain rx_input_wlan0 (1 references)
107 pkts bytes target prot opt in out source destination
108 0 0 all -- any any anywhere anywhere
109)";
110
111// The expected counters for the above output. "* 2" because the same string
112// will be returned for both iptables and ip6tables in the tests.
113const Counter kCounter_eth0{(65571148 + 1767147156ULL) * 2 /*rx_bytes*/,
114 (11683 + 312491) * 2 /*rx_packets*/,
115 (68041668 + 1807550291ULL) * 2 /*tx_bytes*/,
116 (6511 + 202160) * 2 /*tx_packets*/};
117const Counter kCounter_wlan0{0, 0, 96 * 2, 2 * 2};
Jie Jiangcf749152020-07-09 22:20:58 +0900118
119class MockProcessRunner : public MinijailedProcessRunner {
120 public:
121 MockProcessRunner() = default;
122 ~MockProcessRunner() = default;
123
124 MOCK_METHOD(int,
125 iptables,
126 (const std::string& table,
127 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900128 bool log_failures,
129 std::string* output),
Jie Jiangcf749152020-07-09 22:20:58 +0900130 (override));
131 MOCK_METHOD(int,
132 ip6tables,
133 (const std::string& table,
134 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900135 bool log_failures,
136 std::string* output),
Jie Jiangcf749152020-07-09 22:20:58 +0900137 (override));
138};
139
140class CountersServiceTest : public testing::Test {
141 protected:
142 void SetUp() override {
143 fake_shill_client_ = shill_helper_.FakeClient();
144 counters_svc_ =
145 std::make_unique<CountersService>(fake_shill_client_.get(), &runner_);
146 }
147
Jie Jianged0b1cc2020-07-10 15:55:33 +0900148 // Makes `iptables` returning a bad |output|. Expects an empty map from
149 // GetCounters().
150 void TestBadIptablesOutput(const std::string& output) {
151 EXPECT_CALL(runner_, iptables(_, _, _, _))
152 .WillRepeatedly(DoAll(SetArgPointee<3>(output), Return(0)));
153 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
154 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
155
156 auto actual = counters_svc_->GetCounters({});
157 std::map<SourceDevice, Counter> expected;
158
159 EXPECT_THAT(actual, ContainerEq(expected));
160 }
161
162 // Makes `ip6tables` returning a bad |output|. Expects an empty map from
163 // GetCounters().
164 void TestBadIp6tablesOutput(const std::string& output) {
165 EXPECT_CALL(runner_, iptables(_, _, _, _))
166 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
167 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
168 .WillRepeatedly(DoAll(SetArgPointee<3>(output), Return(0)));
169
170 auto actual = counters_svc_->GetCounters({});
171 std::map<SourceDevice, Counter> expected;
172
173 EXPECT_THAT(actual, ContainerEq(expected));
174 }
175
Jie Jiangcf749152020-07-09 22:20:58 +0900176 FakeShillClientHelper shill_helper_;
177 MockProcessRunner runner_;
178 std::unique_ptr<FakeShillClient> fake_shill_client_;
179 std::unique_ptr<CountersService> counters_svc_;
180};
181
182TEST_F(CountersServiceTest, OnNewDevice) {
183 // Makes the check commands return 1 (not found).
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900184 EXPECT_CALL(runner_, iptables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900185 .WillRepeatedly(Return(1));
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900186 EXPECT_CALL(runner_, ip6tables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900187 .WillRepeatedly(Return(1));
188
189 // The following commands are expected when eth0 comes up.
190 const std::vector<std::vector<std::string>> expected_calls{
191 {"-N", "rx_input_eth0", "-w"},
192 {"-A", "INPUT", "-i", "eth0", "-j", "rx_input_eth0", "-w"},
193 {"-A", "rx_input_eth0", "-w"},
194
195 {"-N", "rx_fwd_eth0", "-w"},
196 {"-A", "FORWARD", "-i", "eth0", "-j", "rx_fwd_eth0", "-w"},
197 {"-A", "rx_fwd_eth0", "-w"},
198
199 {"-N", "tx_postrt_eth0", "-w"},
200 {"-A", "POSTROUTING", "-o", "eth0", "-m", "owner", "--socket-exists",
201 "-j", "tx_postrt_eth0", "-w"},
202 {"-A", "tx_postrt_eth0", "-w"},
203
204 {"-N", "tx_fwd_eth0", "-w"},
205 {"-A", "FORWARD", "-o", "eth0", "-j", "tx_fwd_eth0", "-w"},
206 {"-A", "tx_fwd_eth0", "-w"},
207 };
208
209 for (const auto& rule : expected_calls) {
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900210 EXPECT_CALL(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
211 EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
Jie Jiangcf749152020-07-09 22:20:58 +0900212 }
213
214 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
215 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
216 brillo::Any(devices));
217}
218
219TEST_F(CountersServiceTest, OnSameDeviceAppearAgain) {
220 // Makes the check commands return 0 (we already have these rules).
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900221 EXPECT_CALL(runner_, iptables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900222 .WillRepeatedly(Return(0));
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900223 EXPECT_CALL(runner_, ip6tables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900224 .WillRepeatedly(Return(0));
225
226 // Creating chains commands are expected but no more creating rules command
227 // (with "-I" or "-A") should come.
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900228 EXPECT_CALL(runner_, iptables(_, Contains("-N"), _, _)).Times(AnyNumber());
229 EXPECT_CALL(runner_, ip6tables(_, Contains("-N"), _, _)).Times(AnyNumber());
Jie Jiangcf749152020-07-09 22:20:58 +0900230
231 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
232 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
233 brillo::Any(devices));
234}
235
Jie Jianged0b1cc2020-07-10 15:55:33 +0900236TEST_F(CountersServiceTest, QueryTrafficCounters) {
237 EXPECT_CALL(runner_, iptables(_, _, _, _))
238 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
239 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
240 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
241
242 auto actual = counters_svc_->GetCounters({});
243
244 std::map<SourceDevice, Counter> expected{
245 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
246 {{TrafficCounter::UNKNOWN, "wlan0"}, kCounter_wlan0},
247 };
248
249 EXPECT_THAT(actual, ContainerEq(expected));
250}
251
252TEST_F(CountersServiceTest, QueryTrafficCountersWithFilter) {
253 EXPECT_CALL(runner_, iptables(_, _, _, _))
254 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
255 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
256 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
257
258 // Only counters for eth0 should be returned. eth1 should be ignored.
259 auto actual = counters_svc_->GetCounters({"eth0", "eth1"});
260
261 std::map<SourceDevice, Counter> expected{
262 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
263 };
264
265 EXPECT_THAT(actual, ContainerEq(expected));
266}
267
268TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv4Output) {
269 const std::string kEmptyOutput = "";
270 TestBadIptablesOutput(kEmptyOutput);
271}
272
273TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv6Output) {
274 const std::string kEmptyOutput = "";
275 TestBadIp6tablesOutput(kEmptyOutput);
276}
277
278TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainName) {
279 const std::string kBadOutput = R"(
280Chain tx_fwd_eth0 (1 references)
281 pkts bytes target prot opt in out source destination
282 6511 68041668 all -- any any anywhere anywhere
283
284Chain tx_fwd_wlan0 (1 references)
285)";
286 TestBadIptablesOutput(kBadOutput);
287}
288
289TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainNameAndHeader) {
290 const std::string kBadOutput = R"(
291Chain tx_fwd_eth0 (1 references)
292 pkts bytes target prot opt in out source destination
293 6511 68041668 all -- any any anywhere anywhere
294
295Chain tx_fwd_wlan0 (1 references)
296 pkts bytes target prot opt in out source destination
297)";
298 TestBadIptablesOutput(kBadOutput);
299}
300
301TEST_F(CountersServiceTest, QueryTrafficCountersWithNotFinishedCountersLine) {
302 const std::string kBadOutput = R"(
303Chain tx_fwd_eth0 (1 references)
304 pkts bytes target prot opt in out source destination
305 6511 68041668 all -- any any anywhere anywhere
306
307Chain tx_fwd_wlan0 (1 references)
308 pkts bytes target prot opt in out source destination pkts bytes target prot opt in out source destination
309 0 )";
310 TestBadIptablesOutput(kBadOutput);
311}
312
Jie Jiangcf749152020-07-09 22:20:58 +0900313} // namespace
314} // namespace patchpanel