1 /*
2 * Copyright 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * TetherControllerTest.cpp - unit tests for TetherController.cpp
17 */
18
19 #include <string>
20 #include <vector>
21
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include <gtest/gtest.h>
29
30 #include <android-base/stringprintf.h>
31 #include <android-base/strings.h>
32 #include <gmock/gmock.h>
33 #include <netdutils/StatusOr.h>
34
35 #include "IptablesBaseTest.h"
36 #include "OffloadUtils.h"
37 #include "TetherController.h"
38
39 using android::base::Join;
40 using android::base::StringPrintf;
41 using android::bpf::BpfMap;
42 using android::netdutils::StatusOr;
43 using ::testing::Contains;
44 using TetherStats = android::net::TetherController::TetherStats;
45 using TetherStatsList = android::net::TetherController::TetherStatsList;
46 using TetherOffloadStats = android::net::TetherController::TetherOffloadStats;
47 using TetherOffloadStatsList = android::net::TetherController::TetherOffloadStatsList;
48
49 namespace android {
50 namespace net {
51
52 constexpr int TEST_MAP_SIZE = 10;
53
54 // Comparison for TetherOffloadStats. Need to override operator== because class TetherOffloadStats
55 // doesn't have one.
56 // TODO: once C++20 is used, use default operator== in TetherOffloadStats and remove the overriding
57 // here.
operator ==(const TetherOffloadStats & lhs,const TetherOffloadStats & rhs)58 bool operator==(const TetherOffloadStats& lhs, const TetherOffloadStats& rhs) {
59 return lhs.ifIndex == rhs.ifIndex && lhs.rxBytes == rhs.rxBytes && lhs.txBytes == rhs.txBytes &&
60 lhs.rxPackets == rhs.rxPackets && lhs.txPackets == rhs.txPackets;
61 }
62
63 class TetherControllerTest : public IptablesBaseTest {
64 public:
TetherControllerTest()65 TetherControllerTest() {
66 TetherController::iptablesRestoreFunction = fakeExecIptablesRestoreWithOutput;
67 }
68
69 protected:
70 TetherController mTetherCtrl;
71 BpfMap<uint32_t, TetherStatsValue> mFakeTetherStatsMap{BPF_MAP_TYPE_HASH, TEST_MAP_SIZE};
72 BpfMap<uint32_t, uint64_t> mFakeTetherLimitMap{BPF_MAP_TYPE_HASH, TEST_MAP_SIZE};
73
SetUp()74 void SetUp() {
75 SKIP_IF_BPF_NOT_SUPPORTED;
76
77 ASSERT_TRUE(mFakeTetherStatsMap.isValid());
78 ASSERT_TRUE(mFakeTetherLimitMap.isValid());
79
80 mTetherCtrl.mBpfStatsMap = mFakeTetherStatsMap;
81 ASSERT_TRUE(mTetherCtrl.mBpfStatsMap.isValid());
82 mTetherCtrl.mBpfLimitMap = mFakeTetherLimitMap;
83 ASSERT_TRUE(mTetherCtrl.mBpfLimitMap.isValid());
84 }
85
toString(const TetherOffloadStatsList & statsList)86 std::string toString(const TetherOffloadStatsList& statsList) {
87 std::string result;
88 for (const auto& stats : statsList) {
89 result += StringPrintf("%d, %" PRId64 ", %" PRId64 ", %" PRId64 ", %" PRId64 "\n",
90 stats.ifIndex, stats.rxBytes, stats.rxPackets, stats.txBytes,
91 stats.txPackets);
92 }
93 return result;
94 }
95
updateMaps(uint32_t ifaceIndex,uint64_t rxBytes,uint64_t rxPackets,uint64_t txBytes,uint64_t txPackets)96 void updateMaps(uint32_t ifaceIndex, uint64_t rxBytes, uint64_t rxPackets, uint64_t txBytes,
97 uint64_t txPackets) {
98 // {rx, tx}Errors in |tetherStats| are set zero because getTetherStats doesn't use them.
99 const TetherStatsValue tetherStats = {rxPackets, rxBytes, 0 /*unused*/,
100 txPackets, txBytes, 0 /*unused*/};
101 ASSERT_RESULT_OK(mFakeTetherStatsMap.writeValue(ifaceIndex, tetherStats, BPF_ANY));
102 };
103
setDefaults()104 int setDefaults() {
105 return mTetherCtrl.setDefaults();
106 }
107
108 const ExpectedIptablesCommands FLUSH_COMMANDS = {
109 {V4,
110 "*filter\n"
111 ":tetherctrl_FORWARD -\n"
112 "-A tetherctrl_FORWARD -j DROP\n"
113 "COMMIT\n"
114 "*nat\n"
115 ":tetherctrl_nat_POSTROUTING -\n"
116 "COMMIT\n"},
117 {V6,
118 "*filter\n"
119 ":tetherctrl_FORWARD -\n"
120 "COMMIT\n"
121 "*raw\n"
122 ":tetherctrl_raw_PREROUTING -\n"
123 "COMMIT\n"},
124 };
125
126 const ExpectedIptablesCommands SETUP_COMMANDS = {
127 {V4,
128 "*filter\n"
129 ":tetherctrl_FORWARD -\n"
130 "-A tetherctrl_FORWARD -j DROP\n"
131 "COMMIT\n"
132 "*nat\n"
133 ":tetherctrl_nat_POSTROUTING -\n"
134 "COMMIT\n"},
135 {V6,
136 "*filter\n"
137 ":tetherctrl_FORWARD -\n"
138 "COMMIT\n"
139 "*raw\n"
140 ":tetherctrl_raw_PREROUTING -\n"
141 "COMMIT\n"},
142 {V4,
143 "*mangle\n"
144 "-A tetherctrl_mangle_FORWARD -p tcp --tcp-flags SYN SYN "
145 "-j TCPMSS --clamp-mss-to-pmtu\n"
146 "COMMIT\n"},
147 {V4V6,
148 "*filter\n"
149 ":tetherctrl_counters -\n"
150 "COMMIT\n"},
151 };
152
153 const ExpectedIptablesCommands ALERT_ADD_COMMAND = {
154 {V4V6,
155 "*filter\n"
156 "-I tetherctrl_FORWARD -j bw_global_alert\n"
157 "COMMIT\n"},
158 };
159
firstIPv4UpstreamCommands(const char * extIf)160 ExpectedIptablesCommands firstIPv4UpstreamCommands(const char *extIf) {
161 std::string v4Cmd = StringPrintf(
162 "*nat\n"
163 "-A tetherctrl_nat_POSTROUTING -o %s -j MASQUERADE\n"
164 "COMMIT\n", extIf);
165 return {
166 { V4, v4Cmd },
167 };
168 }
169
firstIPv6UpstreamCommands()170 ExpectedIptablesCommands firstIPv6UpstreamCommands() {
171 std::string v6Cmd =
172 "*filter\n"
173 "-A tetherctrl_FORWARD -g tetherctrl_counters\n"
174 "COMMIT\n";
175 return {
176 { V6, v6Cmd },
177 };
178 }
179
180 template<typename T>
appendAll(std::vector<T> & cmds,const std::vector<T> & appendCmds)181 void appendAll(std::vector<T>& cmds, const std::vector<T>& appendCmds) {
182 cmds.insert(cmds.end(), appendCmds.begin(), appendCmds.end());
183 }
184
startNatCommands(const char * intIf,const char * extIf,bool withCounterChainRules)185 ExpectedIptablesCommands startNatCommands(const char *intIf, const char *extIf,
186 bool withCounterChainRules) {
187 std::string rpfilterCmd = StringPrintf(
188 "*raw\n"
189 "-A tetherctrl_raw_PREROUTING -i %s -m rpfilter --invert ! -s fe80::/64 -j DROP\n"
190 "COMMIT\n", intIf);
191
192 std::vector<std::string> v4Cmds = {
193 "*raw",
194 StringPrintf(
195 "-A tetherctrl_raw_PREROUTING -p tcp --dport 21 -i %s -j CT --helper ftp",
196 intIf),
197 StringPrintf("-A tetherctrl_raw_PREROUTING -p tcp --dport 1723 -i %s -j CT "
198 "--helper pptp",
199 intIf),
200 "COMMIT",
201 "*filter",
202 StringPrintf("-A tetherctrl_FORWARD -i %s -o %s -m state --state"
203 " ESTABLISHED,RELATED -g tetherctrl_counters",
204 extIf, intIf),
205 StringPrintf("-A tetherctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
206 intIf, extIf),
207 StringPrintf("-A tetherctrl_FORWARD -i %s -o %s -g tetherctrl_counters", intIf,
208 extIf),
209 };
210
211 std::vector<std::string> v6Cmds = {
212 "*filter",
213 };
214
215 if (withCounterChainRules) {
216 const std::vector<std::string> counterRules = {
217 StringPrintf("-A tetherctrl_counters -i %s -o %s -j RETURN", intIf, extIf),
218 StringPrintf("-A tetherctrl_counters -i %s -o %s -j RETURN", extIf, intIf),
219 };
220
221 appendAll(v4Cmds, counterRules);
222 appendAll(v6Cmds, counterRules);
223 }
224
225 appendAll(v4Cmds, {
226 "-D tetherctrl_FORWARD -j DROP",
227 "-A tetherctrl_FORWARD -j DROP",
228 "COMMIT\n",
229 });
230
231 v6Cmds.push_back("COMMIT\n");
232
233 return {
234 { V6, rpfilterCmd },
235 { V4, Join(v4Cmds, '\n') },
236 { V6, Join(v6Cmds, '\n') },
237 };
238 }
239
240 constexpr static const bool WITH_COUNTERS = true;
241 constexpr static const bool NO_COUNTERS = false;
242 constexpr static const bool WITH_IPV6 = true;
243 constexpr static const bool NO_IPV6 = false;
allNewNatCommands(const char * intIf,const char * extIf,bool withCounterChainRules,bool withIPv6Upstream,bool firstEnableNat)244 ExpectedIptablesCommands allNewNatCommands(const char* intIf, const char* extIf,
245 bool withCounterChainRules, bool withIPv6Upstream,
246 bool firstEnableNat) {
247 ExpectedIptablesCommands commands;
248 ExpectedIptablesCommands setupFirstIPv4Commands = firstIPv4UpstreamCommands(extIf);
249 ExpectedIptablesCommands startFirstNatCommands = startNatCommands(intIf, extIf,
250 withCounterChainRules);
251
252 appendAll(commands, setupFirstIPv4Commands);
253 if (withIPv6Upstream) {
254 ExpectedIptablesCommands setupFirstIPv6Commands = firstIPv6UpstreamCommands();
255 appendAll(commands, setupFirstIPv6Commands);
256 }
257 if (firstEnableNat) {
258 appendAll(commands, ALERT_ADD_COMMAND);
259 }
260 appendAll(commands, startFirstNatCommands);
261
262 return commands;
263 }
264
stopNatCommands(const char * intIf,const char * extIf)265 ExpectedIptablesCommands stopNatCommands(const char *intIf, const char *extIf) {
266 std::string rpfilterCmd = StringPrintf(
267 "*raw\n"
268 "-D tetherctrl_raw_PREROUTING -i %s -m rpfilter --invert ! -s fe80::/64 -j DROP\n"
269 "COMMIT\n", intIf);
270
271 std::vector<std::string> v4Cmds = {
272 "*raw",
273 StringPrintf(
274 "-D tetherctrl_raw_PREROUTING -p tcp --dport 21 -i %s -j CT --helper ftp",
275 intIf),
276 StringPrintf("-D tetherctrl_raw_PREROUTING -p tcp --dport 1723 -i %s -j CT "
277 "--helper pptp",
278 intIf),
279 "COMMIT",
280 "*filter",
281 StringPrintf("-D tetherctrl_FORWARD -i %s -o %s -m state --state"
282 " ESTABLISHED,RELATED -g tetherctrl_counters",
283 extIf, intIf),
284 StringPrintf("-D tetherctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
285 intIf, extIf),
286 StringPrintf("-D tetherctrl_FORWARD -i %s -o %s -g tetherctrl_counters", intIf,
287 extIf),
288 "COMMIT\n",
289 };
290
291 return {
292 { V6, rpfilterCmd },
293 { V4, Join(v4Cmds, '\n') },
294 };
295
296 }
297 };
298
TEST_F(TetherControllerTest,TestSetupIptablesHooks)299 TEST_F(TetherControllerTest, TestSetupIptablesHooks) {
300 mTetherCtrl.setupIptablesHooks();
301 expectIptablesRestoreCommands(SETUP_COMMANDS);
302 }
303
TEST_F(TetherControllerTest,TestSetDefaults)304 TEST_F(TetherControllerTest, TestSetDefaults) {
305 setDefaults();
306 expectIptablesRestoreCommands(FLUSH_COMMANDS);
307 }
308
TEST_F(TetherControllerTest,TestAddAndRemoveNat)309 TEST_F(TetherControllerTest, TestAddAndRemoveNat) {
310 // Start first NAT on first upstream interface. Expect the upstream and NAT rules to be created.
311 ExpectedIptablesCommands firstNat =
312 allNewNatCommands("wlan0", "rmnet0", WITH_COUNTERS, WITH_IPV6, true);
313 mTetherCtrl.enableNat("wlan0", "rmnet0");
314 expectIptablesRestoreCommands(firstNat);
315
316 // Start second NAT on same upstream. Expect only the counter rules to be created.
317 ExpectedIptablesCommands startOtherNatOnSameUpstream = startNatCommands(
318 "usb0", "rmnet0", WITH_COUNTERS);
319 mTetherCtrl.enableNat("usb0", "rmnet0");
320 expectIptablesRestoreCommands(startOtherNatOnSameUpstream);
321
322 // Remove the first NAT.
323 ExpectedIptablesCommands stopFirstNat = stopNatCommands("wlan0", "rmnet0");
324 mTetherCtrl.disableNat("wlan0", "rmnet0");
325 expectIptablesRestoreCommands(stopFirstNat);
326
327 // Remove the last NAT. Expect rules to be cleared.
328 ExpectedIptablesCommands stopLastNat = stopNatCommands("usb0", "rmnet0");
329
330 appendAll(stopLastNat, FLUSH_COMMANDS);
331 mTetherCtrl.disableNat("usb0", "rmnet0");
332 expectIptablesRestoreCommands(stopLastNat);
333
334 // Re-add a NAT removed previously: tetherctrl_counters chain rules are not re-added
335 firstNat = allNewNatCommands("wlan0", "rmnet0", NO_COUNTERS, WITH_IPV6, true);
336 mTetherCtrl.enableNat("wlan0", "rmnet0");
337 expectIptablesRestoreCommands(firstNat);
338
339 // Remove it again. Expect rules to be cleared.
340 stopLastNat = stopNatCommands("wlan0", "rmnet0");
341 appendAll(stopLastNat, FLUSH_COMMANDS);
342 mTetherCtrl.disableNat("wlan0", "rmnet0");
343 expectIptablesRestoreCommands(stopLastNat);
344 }
345
TEST_F(TetherControllerTest,TestMultipleUpstreams)346 TEST_F(TetherControllerTest, TestMultipleUpstreams) {
347 // Start first NAT on first upstream interface. Expect the upstream and NAT rules to be created.
348 ExpectedIptablesCommands firstNat =
349 allNewNatCommands("wlan0", "rmnet0", WITH_COUNTERS, WITH_IPV6, true);
350 mTetherCtrl.enableNat("wlan0", "rmnet0");
351 expectIptablesRestoreCommands(firstNat);
352
353 // Start second NAT, on new upstream. Expect the upstream and NAT rules to be created for IPv4,
354 // but no counter rules for IPv6.
355 ExpectedIptablesCommands secondNat =
356 allNewNatCommands("wlan0", "v4-rmnet0", WITH_COUNTERS, NO_IPV6, false);
357 mTetherCtrl.enableNat("wlan0", "v4-rmnet0");
358 expectIptablesRestoreCommands(secondNat);
359
360 // Pretend that the caller has forgotten that it set up the second NAT, and asks us to do so
361 // again. Expect that we take no action.
362 const ExpectedIptablesCommands NONE = {};
363 mTetherCtrl.enableNat("wlan0", "v4-rmnet0");
364 expectIptablesRestoreCommands(NONE);
365
366 // Remove the second NAT.
367 ExpectedIptablesCommands stopSecondNat = stopNatCommands("wlan0", "v4-rmnet0");
368 mTetherCtrl.disableNat("wlan0", "v4-rmnet0");
369 expectIptablesRestoreCommands(stopSecondNat);
370
371 // Remove the first NAT. Expect rules to be cleared.
372 ExpectedIptablesCommands stopFirstNat = stopNatCommands("wlan0", "rmnet0");
373 appendAll(stopFirstNat, FLUSH_COMMANDS);
374 mTetherCtrl.disableNat("wlan0", "rmnet0");
375 expectIptablesRestoreCommands(stopFirstNat);
376 }
377
378 std::string kTetherCounterHeaders = Join(std::vector<std::string> {
379 "Chain tetherctrl_counters (4 references)",
380 " pkts bytes target prot opt in out source destination",
381 }, '\n');
382
383 std::string kIPv4TetherCounters = Join(std::vector<std::string> {
384 "Chain tetherctrl_counters (4 references)",
385 " pkts bytes target prot opt in out source destination",
386 " 26 2373 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0",
387 " 27 2002 RETURN all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0",
388 " 1040 107471 RETURN all -- bt-pan rmnet0 0.0.0.0/0 0.0.0.0/0",
389 " 1450 1708806 RETURN all -- rmnet0 bt-pan 0.0.0.0/0 0.0.0.0/0",
390 }, '\n');
391
392 std::string kIPv6TetherCounters = Join(std::vector<std::string> {
393 "Chain tetherctrl_counters (2 references)",
394 " pkts bytes target prot opt in out source destination",
395 " 10000 10000000 RETURN all wlan0 rmnet0 ::/0 ::/0",
396 " 20000 20000000 RETURN all rmnet0 wlan0 ::/0 ::/0",
397 }, '\n');
398
expectTetherStatsEqual(const TetherController::TetherStats & expected,const TetherController::TetherStats & actual)399 void expectTetherStatsEqual(const TetherController::TetherStats& expected,
400 const TetherController::TetherStats& actual) {
401 EXPECT_EQ(expected.intIface, actual.intIface);
402 EXPECT_EQ(expected.extIface, actual.extIface);
403 EXPECT_EQ(expected.rxBytes, actual.rxBytes);
404 EXPECT_EQ(expected.txBytes, actual.txBytes);
405 EXPECT_EQ(expected.rxPackets, actual.rxPackets);
406 EXPECT_EQ(expected.txPackets, actual.txPackets);
407 }
408
TEST_F(TetherControllerTest,TestGetTetherStats)409 TEST_F(TetherControllerTest, TestGetTetherStats) {
410 // Finding no headers is an error.
411 ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
412 clearIptablesRestoreOutput();
413
414 // Finding only v4 or only v6 headers is an error.
415 addIptablesRestoreOutput(kTetherCounterHeaders, "");
416 ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
417 clearIptablesRestoreOutput();
418
419 addIptablesRestoreOutput("", kTetherCounterHeaders);
420 ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
421 clearIptablesRestoreOutput();
422
423 // Finding headers but no stats is not an error.
424 addIptablesRestoreOutput(kTetherCounterHeaders, kTetherCounterHeaders);
425 StatusOr<TetherStatsList> result = mTetherCtrl.getTetherStats();
426 ASSERT_TRUE(isOk(result));
427 TetherStatsList actual = result.value();
428 ASSERT_EQ(0U, actual.size());
429 clearIptablesRestoreOutput();
430
431
432 addIptablesRestoreOutput(kIPv6TetherCounters);
433 ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
434 clearIptablesRestoreOutput();
435
436 // IPv4 and IPv6 counters are properly added together.
437 addIptablesRestoreOutput(kIPv4TetherCounters, kIPv6TetherCounters);
438 TetherStats expected0("wlan0", "rmnet0", 20002002, 20027, 10002373, 10026);
439 TetherStats expected1("bt-pan", "rmnet0", 1708806, 1450, 107471, 1040);
440 result = mTetherCtrl.getTetherStats();
441 ASSERT_TRUE(isOk(result));
442 actual = result.value();
443 ASSERT_EQ(2U, actual.size());
444 expectTetherStatsEqual(expected0, result.value()[0]);
445 expectTetherStatsEqual(expected1, result.value()[1]);
446 clearIptablesRestoreOutput();
447
448 // No stats: error.
449 addIptablesRestoreOutput("", kIPv6TetherCounters);
450 ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
451 clearIptablesRestoreOutput();
452
453 addIptablesRestoreOutput(kIPv4TetherCounters, "");
454 ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
455 clearIptablesRestoreOutput();
456
457 // Include only one pair of interfaces and things are fine.
458 std::vector<std::string> counterLines = android::base::Split(kIPv4TetherCounters, "\n");
459 std::vector<std::string> brokenCounterLines = counterLines;
460 counterLines.resize(4);
461 std::string counters = Join(counterLines, "\n") + "\n";
462 addIptablesRestoreOutput(counters, counters);
463 TetherStats expected1_0("wlan0", "rmnet0", 4004, 54, 4746, 52);
464 result = mTetherCtrl.getTetherStats();
465 ASSERT_TRUE(isOk(result));
466 actual = result.value();
467 ASSERT_EQ(1U, actual.size());
468 expectTetherStatsEqual(expected1_0, actual[0]);
469 clearIptablesRestoreOutput();
470
471 // But if interfaces aren't paired, it's always an error.
472 counterLines.resize(3);
473 counters = Join(counterLines, "\n") + "\n";
474 addIptablesRestoreOutput(counters, counters);
475 result = mTetherCtrl.getTetherStats();
476 ASSERT_FALSE(isOk(result));
477 clearIptablesRestoreOutput();
478
479 // Token unit test of the fact that we return the stats in the error message which the caller
480 // ignores.
481 // Skip header since we only saved the last line we parsed.
482 std::string expectedError = counterLines[2];
483 std::string err = result.status().msg();
484 ASSERT_LE(expectedError.size(), err.size());
485 EXPECT_TRUE(std::equal(expectedError.rbegin(), expectedError.rend(), err.rbegin()));
486 }
487
TEST_F(TetherControllerTest,TestTetherOffloadGetStats)488 TEST_F(TetherControllerTest, TestTetherOffloadGetStats) {
489 SKIP_IF_BPF_NOT_SUPPORTED;
490
491 updateMaps(101, 100, 10, 200, 20);
492 updateMaps(102, 300, 30, 400, 40);
493 const TetherOffloadStats expected0{101, 100, 10, 200, 20};
494 const TetherOffloadStats expected1{102, 300, 30, 400, 40};
495
496 const StatusOr<TetherOffloadStatsList> result = mTetherCtrl.getTetherOffloadStats();
497 ASSERT_OK(result);
498 const TetherOffloadStatsList& actual = result.value();
499 ASSERT_EQ(2U, actual.size());
500 EXPECT_THAT(actual, Contains(expected0)) << toString(actual);
501 EXPECT_THAT(actual, Contains(expected1)) << toString(actual);
502 clearIptablesRestoreOutput();
503 }
504
TEST_F(TetherControllerTest,TestTetherOffloadSetQuota)505 TEST_F(TetherControllerTest, TestTetherOffloadSetQuota) {
506 SKIP_IF_BPF_NOT_SUPPORTED;
507
508 const uint32_t ifindex = 100;
509 const uint64_t minQuota = 0;
510 const uint64_t maxQuota = std::numeric_limits<int64_t>::max();
511 const uint64_t infinityQuota = std::numeric_limits<uint64_t>::max();
512
513 // Create a stats entry with zeroes in the first time set limit.
514 ASSERT_EQ(0, mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, minQuota));
515 const StatusOr<TetherOffloadStatsList> result = mTetherCtrl.getTetherOffloadStats();
516 ASSERT_OK(result);
517 const TetherOffloadStatsList& actual = result.value();
518 ASSERT_EQ(1U, actual.size());
519 EXPECT_THAT(actual, Contains(TetherOffloadStats{ifindex, 0, 0, 0, 0})) << toString(actual);
520
521 // Verify the quota with the boundary {min, max, infinity}.
522 const uint64_t rxBytes = 1000;
523 const uint64_t txBytes = 2000;
524 updateMaps(ifindex, rxBytes, 0 /*unused*/, txBytes, 0 /*unused*/);
525
526 for (const uint64_t quota : {minQuota, maxQuota, infinityQuota}) {
527 ASSERT_EQ(0, mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, quota));
528 base::Result<uint64_t> result = mFakeTetherLimitMap.readValue(ifindex);
529 ASSERT_RESULT_OK(result);
530
531 const uint64_t expectedQuota =
532 (quota == infinityQuota) ? infinityQuota : quota + rxBytes + txBytes;
533 EXPECT_EQ(expectedQuota, result.value());
534 }
535
536 // The valid range of interface index is 1..max_int64.
537 const uint32_t invalidIfindex = 0;
538 int ret = mTetherCtrl.setTetherOffloadInterfaceQuota(invalidIfindex /*bad*/, infinityQuota);
539 ASSERT_EQ(-ENODEV, ret);
540
541 // The valid range of quota is 0..max_int64 or -1 (unlimited).
542 const uint64_t invalidQuota = std::numeric_limits<int64_t>::min();
543 ret = mTetherCtrl.setTetherOffloadInterfaceQuota(ifindex, invalidQuota /*bad*/);
544 ASSERT_EQ(-ERANGE, ret);
545 }
546
547 } // namespace net
548 } // namespace android
549