blob: a566b527b37c68c24fd8001f9cc63ade8c315801 [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"
17
18namespace patchpanel {
Jie Jiangcf749152020-07-09 22:20:58 +090019
Jie Jianged0b1cc2020-07-10 15:55:33 +090020using ::testing::ContainerEq;
Jie Jiangcf749152020-07-09 22:20:58 +090021using ::testing::Contains;
Jie Jianged0b1cc2020-07-10 15:55:33 +090022using ::testing::DoAll;
Jie Jiang8fd0ace2020-09-01 14:51:57 +090023using ::testing::Each;
Jie Jiangcf749152020-07-09 22:20:58 +090024using ::testing::ElementsAreArray;
Jie Jiang8fd0ace2020-09-01 14:51:57 +090025using ::testing::Lt;
Jie Jianged0b1cc2020-07-10 15:55:33 +090026using ::testing::Return;
27using ::testing::SetArgPointee;
Jie Jiang8fd0ace2020-09-01 14:51:57 +090028using ::testing::SizeIs;
Jie Jianged0b1cc2020-07-10 15:55:33 +090029
30using Counter = CountersService::Counter;
31using SourceDevice = CountersService::SourceDevice;
32
33// The following two functions should be put outside the anounymous namespace
34// otherwise they could not be found in the tests.
35std::ostream& operator<<(std::ostream& os, const Counter& counter) {
36 os << "rx_bytes:" << counter.rx_bytes << ", rx_packets:" << counter.rx_packets
37 << ", tx_bytes:" << counter.tx_bytes
38 << ", tx_packets:" << counter.tx_packets;
39 return os;
40}
41
42bool operator==(const CountersService::Counter lhs,
43 const CountersService::Counter rhs) {
44 return lhs.rx_bytes == rhs.rx_bytes && lhs.rx_packets == rhs.rx_packets &&
45 lhs.tx_bytes == rhs.tx_bytes && lhs.tx_packets == rhs.tx_packets;
46}
47
48namespace {
49// The following string is copied from the real output of iptables v1.6.2 by
50// `iptables -t mangle -L -x -v`. This output contains all the accounting
51// chains/rules for eth0 and wlan0.
52// TODO(jiejiang): presubmit checker is complaining about the line length for
53// this (and the other raw strings in this file). Find a way to make it happy.
54const char kIptablesOutput[] = R"(
55Chain PREROUTING (policy ACCEPT 22785 packets, 136093545 bytes)
56 pkts bytes target prot opt in out source destination
57 18 2196 MARK all -- arcbr0 any anywhere anywhere MARK set 0x1
58 0 0 MARK all -- vmtap+ any anywhere anywhere MARK set 0x1
59 6526 68051766 MARK all -- arc_eth0 any anywhere anywhere MARK set 0x1
60 9 1104 MARK all -- arc_wlan0 any anywhere anywhere MARK set 0x1
61
62Chain INPUT (policy ACCEPT 4421 packets, 2461233 bytes)
63 pkts bytes target prot opt in out source destination
64 312491 1767147156 rx_input_eth0 all -- eth0 any anywhere anywhere
65 0 0 rx_input_wlan0 all -- wlan0 any anywhere anywhere
66
67Chain FORWARD (policy ACCEPT 18194 packets, 133612816 bytes)
68 pkts bytes target prot opt in out source destination
69 6511 68041668 tx_fwd_eth0 all -- any eth0 anywhere anywhere
70 11683 65571148 rx_fwd_eth0 all -- eth0 any anywhere anywhere
71 0 0 tx_fwd_wlan0 all -- any wlan0 anywhere anywhere
72 0 0 rx_fwd_wlan0 all -- wlan0 any anywhere anywhere
73
74Chain OUTPUT (policy ACCEPT 4574 packets, 2900995 bytes)
75 pkts bytes target prot opt in out source destination
76
77Chain POSTROUTING (policy ACCEPT 22811 packets, 136518827 bytes)
78 pkts bytes target prot opt in out source destination
79 202160 1807550291 tx_postrt_eth0 all -- any eth0 anywhere anywhere owner socket exists
80 2 96 tx_postrt_wlan0 all -- any wlan0 anywhere anywhere owner socket exists
81
82Chain tx_fwd_eth0 (1 references)
83 pkts bytes target prot opt in out source destination
84 6511 68041668 all -- any any anywhere anywhere
85
86Chain tx_fwd_wlan0 (1 references)
87 pkts bytes target prot opt in out source destination
88 0 0 all -- any any anywhere anywhere
89
90Chain tx_postrt_eth0 (1 references)
91 pkts bytes target prot opt in out source destination
92 202160 1807550291 all -- any any anywhere anywhere
93
94Chain tx_postrt_wlan0 (1 references)
95 pkts bytes target prot opt in out source destination
96 2 96 all -- any any anywhere anywhere
97
98Chain rx_fwd_eth0 (1 references)
99 pkts bytes target prot opt in out source destination
100 11683 65571148 all -- any any anywhere anywhere
101
102Chain rx_fwd_wlan0 (1 references)
103 pkts bytes target prot opt in out source destination
104 0 0 all -- any any anywhere anywhere
105
106Chain rx_input_eth0 (1 references)
107 pkts bytes target prot opt in out source destination
108 312491 1767147156 all -- any any anywhere anywhere
109
110Chain rx_input_wlan0 (1 references)
111 pkts bytes target prot opt in out source destination
112 0 0 all -- any any anywhere anywhere
113)";
114
115// The expected counters for the above output. "* 2" because the same string
116// will be returned for both iptables and ip6tables in the tests.
117const Counter kCounter_eth0{(65571148 + 1767147156ULL) * 2 /*rx_bytes*/,
118 (11683 + 312491) * 2 /*rx_packets*/,
119 (68041668 + 1807550291ULL) * 2 /*tx_bytes*/,
120 (6511 + 202160) * 2 /*tx_packets*/};
121const Counter kCounter_wlan0{0, 0, 96 * 2, 2 * 2};
Jie Jiangcf749152020-07-09 22:20:58 +0900122
123class MockProcessRunner : public MinijailedProcessRunner {
124 public:
125 MockProcessRunner() = default;
126 ~MockProcessRunner() = default;
127
128 MOCK_METHOD(int,
129 iptables,
130 (const std::string& table,
131 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900132 bool log_failures,
133 std::string* output),
Jie Jiangcf749152020-07-09 22:20:58 +0900134 (override));
135 MOCK_METHOD(int,
136 ip6tables,
137 (const std::string& table,
138 const std::vector<std::string>& argv,
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900139 bool log_failures,
140 std::string* output),
Jie Jiangcf749152020-07-09 22:20:58 +0900141 (override));
142};
143
144class CountersServiceTest : public testing::Test {
145 protected:
146 void SetUp() override {
147 fake_shill_client_ = shill_helper_.FakeClient();
148 counters_svc_ =
149 std::make_unique<CountersService>(fake_shill_client_.get(), &runner_);
150 }
151
Jie Jianged0b1cc2020-07-10 15:55:33 +0900152 // Makes `iptables` returning a bad |output|. Expects an empty map from
153 // GetCounters().
154 void TestBadIptablesOutput(const std::string& output) {
155 EXPECT_CALL(runner_, iptables(_, _, _, _))
156 .WillRepeatedly(DoAll(SetArgPointee<3>(output), Return(0)));
157 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
158 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
159
160 auto actual = counters_svc_->GetCounters({});
161 std::map<SourceDevice, Counter> expected;
162
163 EXPECT_THAT(actual, ContainerEq(expected));
164 }
165
166 // Makes `ip6tables` returning a bad |output|. Expects an empty map from
167 // GetCounters().
168 void TestBadIp6tablesOutput(const std::string& output) {
169 EXPECT_CALL(runner_, iptables(_, _, _, _))
170 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
171 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
172 .WillRepeatedly(DoAll(SetArgPointee<3>(output), Return(0)));
173
174 auto actual = counters_svc_->GetCounters({});
175 std::map<SourceDevice, Counter> expected;
176
177 EXPECT_THAT(actual, ContainerEq(expected));
178 }
179
Jie Jiangcf749152020-07-09 22:20:58 +0900180 FakeShillClientHelper shill_helper_;
181 MockProcessRunner runner_;
182 std::unique_ptr<FakeShillClient> fake_shill_client_;
183 std::unique_ptr<CountersService> counters_svc_;
184};
185
186TEST_F(CountersServiceTest, OnNewDevice) {
187 // Makes the check commands return 1 (not found).
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900188 EXPECT_CALL(runner_, iptables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900189 .WillRepeatedly(Return(1));
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900190 EXPECT_CALL(runner_, ip6tables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900191 .WillRepeatedly(Return(1));
192
193 // The following commands are expected when eth0 comes up.
194 const std::vector<std::vector<std::string>> expected_calls{
195 {"-N", "rx_input_eth0", "-w"},
196 {"-A", "INPUT", "-i", "eth0", "-j", "rx_input_eth0", "-w"},
197 {"-A", "rx_input_eth0", "-w"},
198
199 {"-N", "rx_fwd_eth0", "-w"},
200 {"-A", "FORWARD", "-i", "eth0", "-j", "rx_fwd_eth0", "-w"},
201 {"-A", "rx_fwd_eth0", "-w"},
202
203 {"-N", "tx_postrt_eth0", "-w"},
204 {"-A", "POSTROUTING", "-o", "eth0", "-m", "owner", "--socket-exists",
205 "-j", "tx_postrt_eth0", "-w"},
206 {"-A", "tx_postrt_eth0", "-w"},
207
208 {"-N", "tx_fwd_eth0", "-w"},
209 {"-A", "FORWARD", "-o", "eth0", "-j", "tx_fwd_eth0", "-w"},
210 {"-A", "tx_fwd_eth0", "-w"},
211 };
212
213 for (const auto& rule : expected_calls) {
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900214 EXPECT_CALL(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
215 EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
Jie Jiangcf749152020-07-09 22:20:58 +0900216 }
217
218 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
219 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
220 brillo::Any(devices));
221}
222
223TEST_F(CountersServiceTest, OnSameDeviceAppearAgain) {
224 // Makes the check commands return 0 (we already have these rules).
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900225 EXPECT_CALL(runner_, iptables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900226 .WillRepeatedly(Return(0));
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900227 EXPECT_CALL(runner_, ip6tables(_, Contains("-C"), _, _))
Jie Jiangcf749152020-07-09 22:20:58 +0900228 .WillRepeatedly(Return(0));
229
230 // Creating chains commands are expected but no more creating rules command
231 // (with "-I" or "-A") should come.
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900232 EXPECT_CALL(runner_, iptables(_, Contains("-N"), _, _)).Times(AnyNumber());
233 EXPECT_CALL(runner_, ip6tables(_, Contains("-N"), _, _)).Times(AnyNumber());
Jie Jiangcf749152020-07-09 22:20:58 +0900234
235 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
236 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
237 brillo::Any(devices));
238}
239
Jie Jiang8fd0ace2020-09-01 14:51:57 +0900240TEST_F(CountersServiceTest, ChainNameLength) {
241 // Makes the check commands return 1 (not found).
242 EXPECT_CALL(runner_, iptables(_, Contains("-C"), _, _))
243 .WillRepeatedly(Return(1));
244 EXPECT_CALL(runner_, ip6tables(_, Contains("-C"), _, _))
245 .WillRepeatedly(Return(1));
246
247 // The name of a new chain must be shorter than 29 characters, otherwise
248 // iptables will reject the request. Uses Each() here for simplicity since no
249 // other params could be longer than 29 for now.
250 static constexpr int kMaxChainNameLength = 29;
251 EXPECT_CALL(runner_, iptables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
252 .Times(AnyNumber());
253 EXPECT_CALL(runner_,
254 ip6tables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
255 .Times(AnyNumber());
256
257 static const std::string kLongInterfaceName(IFNAMSIZ, 'a');
258 std::vector<dbus::ObjectPath> devices = {
259 dbus::ObjectPath("/device/" + kLongInterfaceName)};
260 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
261 brillo::Any(devices));
262}
263
Jie Jianged0b1cc2020-07-10 15:55:33 +0900264TEST_F(CountersServiceTest, QueryTrafficCounters) {
265 EXPECT_CALL(runner_, iptables(_, _, _, _))
266 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
267 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
268 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
269
270 auto actual = counters_svc_->GetCounters({});
271
272 std::map<SourceDevice, Counter> expected{
273 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
274 {{TrafficCounter::UNKNOWN, "wlan0"}, kCounter_wlan0},
275 };
276
277 EXPECT_THAT(actual, ContainerEq(expected));
278}
279
280TEST_F(CountersServiceTest, QueryTrafficCountersWithFilter) {
281 EXPECT_CALL(runner_, iptables(_, _, _, _))
282 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
283 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
284 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
285
286 // Only counters for eth0 should be returned. eth1 should be ignored.
287 auto actual = counters_svc_->GetCounters({"eth0", "eth1"});
288
289 std::map<SourceDevice, Counter> expected{
290 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
291 };
292
293 EXPECT_THAT(actual, ContainerEq(expected));
294}
295
296TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv4Output) {
297 const std::string kEmptyOutput = "";
298 TestBadIptablesOutput(kEmptyOutput);
299}
300
301TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv6Output) {
302 const std::string kEmptyOutput = "";
303 TestBadIp6tablesOutput(kEmptyOutput);
304}
305
306TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainName) {
307 const std::string kBadOutput = R"(
308Chain tx_fwd_eth0 (1 references)
309 pkts bytes target prot opt in out source destination
310 6511 68041668 all -- any any anywhere anywhere
311
312Chain tx_fwd_wlan0 (1 references)
313)";
314 TestBadIptablesOutput(kBadOutput);
315}
316
317TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainNameAndHeader) {
318 const std::string kBadOutput = R"(
319Chain tx_fwd_eth0 (1 references)
320 pkts bytes target prot opt in out source destination
321 6511 68041668 all -- any any anywhere anywhere
322
323Chain tx_fwd_wlan0 (1 references)
324 pkts bytes target prot opt in out source destination
325)";
326 TestBadIptablesOutput(kBadOutput);
327}
328
329TEST_F(CountersServiceTest, QueryTrafficCountersWithNotFinishedCountersLine) {
330 const std::string kBadOutput = R"(
331Chain tx_fwd_eth0 (1 references)
332 pkts bytes target prot opt in out source destination
333 6511 68041668 all -- any any anywhere anywhere
334
335Chain tx_fwd_wlan0 (1 references)
336 pkts bytes target prot opt in out source destination pkts bytes target prot opt in out source destination
337 0 )";
338 TestBadIptablesOutput(kBadOutput);
339}
340
Jie Jiangcf749152020-07-09 22:20:58 +0900341} // namespace
342} // namespace patchpanel