blob: 72a31181a58590c8b14adbb242abc76fc91bb650 [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"},
Hugo Benichiaf2021b2020-11-20 14:07:16 +0900176 {"-N", "tx_eth0", "-w"},
Hugo Benichi33af8f72020-11-20 10:08:47 +0900177 {"-A", "INPUT", "-i", "eth0", "-j", "rx_eth0", "-w"},
178 {"-A", "FORWARD", "-i", "eth0", "-j", "rx_eth0", "-w"},
Hugo Benichi33af8f72020-11-20 10:08:47 +0900179 {"-A", "POSTROUTING", "-o", "eth0", "-j", "tx_eth0", "-w"},
Hugo Benichiaf2021b2020-11-20 14:07:16 +0900180 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
181 "RETURN", "-w"},
182 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
183 "RETURN", "-w"},
184 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
185 "RETURN", "-w"},
186 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
187 "RETURN", "-w"},
188 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
189 "RETURN", "-w"},
190 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
191 "RETURN", "-w"},
192 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
193 "RETURN", "-w"},
194 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
195 "RETURN", "-w"},
196 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
197 "RETURN", "-w"},
198 {"-A", "tx_eth0", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
199 "RETURN", "-w"},
200 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000100/0x00003f00", "-j",
201 "RETURN", "-w"},
202 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000200/0x00003f00", "-j",
203 "RETURN", "-w"},
204 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000300/0x00003f00", "-j",
205 "RETURN", "-w"},
206 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000400/0x00003f00", "-j",
207 "RETURN", "-w"},
208 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00000500/0x00003f00", "-j",
209 "RETURN", "-w"},
210 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002000/0x00003f00", "-j",
211 "RETURN", "-w"},
212 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002100/0x00003f00", "-j",
213 "RETURN", "-w"},
214 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002200/0x00003f00", "-j",
215 "RETURN", "-w"},
216 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002300/0x00003f00", "-j",
217 "RETURN", "-w"},
218 {"-A", "rx_eth0", "-m", "mark", "--mark", "0x00002400/0x00003f00", "-j",
219 "RETURN", "-w"},
Jie Jiangcf749152020-07-09 22:20:58 +0900220 };
221
222 for (const auto& rule : expected_calls) {
Jie Jiangcf5ce9c2020-07-14 17:22:03 +0900223 EXPECT_CALL(runner_, iptables("mangle", ElementsAreArray(rule), _, _));
224 EXPECT_CALL(runner_, ip6tables("mangle", ElementsAreArray(rule), _, _));
Jie Jiangcf749152020-07-09 22:20:58 +0900225 }
226
227 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
228 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
229 brillo::Any(devices));
230}
231
232TEST_F(CountersServiceTest, OnSameDeviceAppearAgain) {
Hugo Benichiddf00842020-11-20 10:24:08 +0900233 // Makes the chain creation commands return false (we already have these
234 // rules).
235 EXPECT_CALL(runner_, iptables(_, Contains("-N"), _, _))
236 .WillRepeatedly(Return(1));
237 EXPECT_CALL(runner_, ip6tables(_, Contains("-N"), _, _))
238 .WillRepeatedly(Return(1));
Jie Jiangcf749152020-07-09 22:20:58 +0900239
240 // Creating chains commands are expected but no more creating rules command
241 // (with "-I" or "-A") should come.
Hugo Benichiddf00842020-11-20 10:24:08 +0900242 EXPECT_CALL(runner_, iptables(_, Contains("-A"), _, _)).Times(0);
243 EXPECT_CALL(runner_, ip6tables(_, Contains("-A"), _, _)).Times(0);
244 EXPECT_CALL(runner_, iptables(_, Contains("-I"), _, _)).Times(0);
245 EXPECT_CALL(runner_, ip6tables(_, Contains("-I"), _, _)).Times(0);
Jie Jiangcf749152020-07-09 22:20:58 +0900246
247 std::vector<dbus::ObjectPath> devices = {dbus::ObjectPath("/device/eth0")};
248 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
249 brillo::Any(devices));
250}
251
Jie Jiang8fd0ace2020-09-01 14:51:57 +0900252TEST_F(CountersServiceTest, ChainNameLength) {
Jie Jiang8fd0ace2020-09-01 14:51:57 +0900253 // The name of a new chain must be shorter than 29 characters, otherwise
254 // iptables will reject the request. Uses Each() here for simplicity since no
255 // other params could be longer than 29 for now.
256 static constexpr int kMaxChainNameLength = 29;
257 EXPECT_CALL(runner_, iptables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
258 .Times(AnyNumber());
259 EXPECT_CALL(runner_,
260 ip6tables(_, Each(SizeIs(Lt(kMaxChainNameLength))), _, _))
261 .Times(AnyNumber());
262
263 static const std::string kLongInterfaceName(IFNAMSIZ, 'a');
264 std::vector<dbus::ObjectPath> devices = {
265 dbus::ObjectPath("/device/" + kLongInterfaceName)};
266 fake_shill_client_->NotifyManagerPropertyChange(shill::kDevicesProperty,
267 brillo::Any(devices));
268}
269
Jie Jianged0b1cc2020-07-10 15:55:33 +0900270TEST_F(CountersServiceTest, QueryTrafficCounters) {
271 EXPECT_CALL(runner_, iptables(_, _, _, _))
272 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
273 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
274 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
275
276 auto actual = counters_svc_->GetCounters({});
277
278 std::map<SourceDevice, Counter> expected{
279 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
280 {{TrafficCounter::UNKNOWN, "wlan0"}, kCounter_wlan0},
281 };
282
283 EXPECT_THAT(actual, ContainerEq(expected));
284}
285
286TEST_F(CountersServiceTest, QueryTrafficCountersWithFilter) {
287 EXPECT_CALL(runner_, iptables(_, _, _, _))
288 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
289 EXPECT_CALL(runner_, ip6tables(_, _, _, _))
290 .WillRepeatedly(DoAll(SetArgPointee<3>(kIptablesOutput), Return(0)));
291
292 // Only counters for eth0 should be returned. eth1 should be ignored.
293 auto actual = counters_svc_->GetCounters({"eth0", "eth1"});
294
295 std::map<SourceDevice, Counter> expected{
296 {{TrafficCounter::UNKNOWN, "eth0"}, kCounter_eth0},
297 };
298
299 EXPECT_THAT(actual, ContainerEq(expected));
300}
301
302TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv4Output) {
303 const std::string kEmptyOutput = "";
304 TestBadIptablesOutput(kEmptyOutput);
305}
306
307TEST_F(CountersServiceTest, QueryTrafficCountersWithEmptyIPv6Output) {
308 const std::string kEmptyOutput = "";
309 TestBadIp6tablesOutput(kEmptyOutput);
310}
311
312TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainName) {
313 const std::string kBadOutput = R"(
314Chain tx_fwd_eth0 (1 references)
315 pkts bytes target prot opt in out source destination
316 6511 68041668 all -- any any anywhere anywhere
317
318Chain tx_fwd_wlan0 (1 references)
319)";
320 TestBadIptablesOutput(kBadOutput);
321}
322
323TEST_F(CountersServiceTest, QueryTrafficCountersWithOnlyChainNameAndHeader) {
324 const std::string kBadOutput = R"(
325Chain tx_fwd_eth0 (1 references)
326 pkts bytes target prot opt in out source destination
327 6511 68041668 all -- any any anywhere anywhere
328
329Chain tx_fwd_wlan0 (1 references)
330 pkts bytes target prot opt in out source destination
331)";
332 TestBadIptablesOutput(kBadOutput);
333}
334
335TEST_F(CountersServiceTest, QueryTrafficCountersWithNotFinishedCountersLine) {
336 const std::string kBadOutput = R"(
337Chain tx_fwd_eth0 (1 references)
338 pkts bytes target prot opt in out source destination
339 6511 68041668 all -- any any anywhere anywhere
340
341Chain tx_fwd_wlan0 (1 references)
342 pkts bytes target prot opt in out source destination pkts bytes target prot opt in out source destination
343 0 )";
344 TestBadIptablesOutput(kBadOutput);
345}
346
Jie Jiangcf749152020-07-09 22:20:58 +0900347} // namespace
348} // namespace patchpanel