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