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::netdutils::StatusOr;
42 using TetherStats = android::net::TetherController::TetherStats;
43 using TetherStatsList = android::net::TetherController::TetherStatsList;
44 
45 namespace android {
46 namespace net {
47 
48 class TetherControllerTest : public IptablesBaseTest {
49 public:
TetherControllerTest()50     TetherControllerTest() {
51         TetherController::iptablesRestoreFunction = fakeExecIptablesRestoreWithOutput;
52     }
53 
54 protected:
55     TetherController mTetherCtrl;
56 
setDefaults()57     int setDefaults() {
58         return mTetherCtrl.setDefaults();
59     }
60 
61     const ExpectedIptablesCommands FLUSH_COMMANDS = {
62             {V4,
63              "*filter\n"
64              ":tetherctrl_FORWARD -\n"
65              "-A tetherctrl_FORWARD -j DROP\n"
66              "COMMIT\n"
67              "*nat\n"
68              ":tetherctrl_nat_POSTROUTING -\n"
69              "COMMIT\n"},
70             {V6,
71              "*filter\n"
72              ":tetherctrl_FORWARD -\n"
73              "COMMIT\n"
74              "*raw\n"
75              ":tetherctrl_raw_PREROUTING -\n"
76              "COMMIT\n"},
77     };
78 
79     const ExpectedIptablesCommands SETUP_COMMANDS = {
80             {V4,
81              "*filter\n"
82              ":tetherctrl_FORWARD -\n"
83              "-A tetherctrl_FORWARD -j DROP\n"
84              "COMMIT\n"
85              "*nat\n"
86              ":tetherctrl_nat_POSTROUTING -\n"
87              "COMMIT\n"},
88             {V6,
89              "*filter\n"
90              ":tetherctrl_FORWARD -\n"
91              "COMMIT\n"
92              "*raw\n"
93              ":tetherctrl_raw_PREROUTING -\n"
94              "COMMIT\n"},
95             {V4,
96              "*mangle\n"
97              "-A tetherctrl_mangle_FORWARD -p tcp --tcp-flags SYN SYN "
98              "-j TCPMSS --clamp-mss-to-pmtu\n"
99              "COMMIT\n"},
100             {V4V6,
101              "*filter\n"
102              ":tetherctrl_counters -\n"
103              "COMMIT\n"},
104     };
105 
106     const ExpectedIptablesCommands ALERT_ADD_COMMAND = {
107             {V4V6,
108              "*filter\n"
109              "-I tetherctrl_FORWARD -j bw_global_alert\n"
110              "COMMIT\n"},
111     };
112 
firstIPv4UpstreamCommands(const char * extIf)113     ExpectedIptablesCommands firstIPv4UpstreamCommands(const char *extIf) {
114         std::string v4Cmd = StringPrintf(
115             "*nat\n"
116             "-A tetherctrl_nat_POSTROUTING -o %s -j MASQUERADE\n"
117             "COMMIT\n", extIf);
118         return {
119             { V4, v4Cmd },
120         };
121     }
122 
firstIPv6UpstreamCommands()123     ExpectedIptablesCommands firstIPv6UpstreamCommands() {
124         std::string v6Cmd =
125                 "*filter\n"
126                 "-A tetherctrl_FORWARD -g tetherctrl_counters\n"
127                 "COMMIT\n";
128         return {
129             { V6, v6Cmd },
130         };
131     }
132 
133     template<typename T>
appendAll(std::vector<T> & cmds,const std::vector<T> & appendCmds)134     void appendAll(std::vector<T>& cmds, const std::vector<T>& appendCmds) {
135         cmds.insert(cmds.end(), appendCmds.begin(), appendCmds.end());
136     }
137 
startNatCommands(const char * intIf,const char * extIf,bool withCounterChainRules)138     ExpectedIptablesCommands startNatCommands(const char *intIf, const char *extIf,
139             bool withCounterChainRules) {
140         std::string rpfilterCmd = StringPrintf(
141             "*raw\n"
142             "-A tetherctrl_raw_PREROUTING -i %s -m rpfilter --invert ! -s fe80::/64 -j DROP\n"
143             "COMMIT\n", intIf);
144 
145         std::vector<std::string> v4Cmds = {
146                 "*raw",
147                 StringPrintf(
148                         "-A tetherctrl_raw_PREROUTING -p tcp --dport 21 -i %s -j CT --helper ftp",
149                         intIf),
150                 StringPrintf("-A tetherctrl_raw_PREROUTING -p tcp --dport 1723 -i %s -j CT "
151                              "--helper pptp",
152                              intIf),
153                 "COMMIT",
154                 "*filter",
155                 StringPrintf("-A tetherctrl_FORWARD -i %s -o %s -m state --state"
156                              " ESTABLISHED,RELATED -g tetherctrl_counters",
157                              extIf, intIf),
158                 StringPrintf("-A tetherctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
159                              intIf, extIf),
160                 StringPrintf("-A tetherctrl_FORWARD -i %s -o %s -g tetherctrl_counters", intIf,
161                              extIf),
162         };
163 
164         std::vector<std::string> v6Cmds = {
165             "*filter",
166         };
167 
168         if (withCounterChainRules) {
169             const std::vector<std::string> counterRules = {
170                 StringPrintf("-A tetherctrl_counters -i %s -o %s -j RETURN", intIf, extIf),
171                 StringPrintf("-A tetherctrl_counters -i %s -o %s -j RETURN", extIf, intIf),
172             };
173 
174             appendAll(v4Cmds, counterRules);
175             appendAll(v6Cmds, counterRules);
176         }
177 
178         appendAll(v4Cmds, {
179             "-D tetherctrl_FORWARD -j DROP",
180             "-A tetherctrl_FORWARD -j DROP",
181             "COMMIT\n",
182         });
183 
184         v6Cmds.push_back("COMMIT\n");
185 
186         return {
187             { V6, rpfilterCmd },
188             { V4, Join(v4Cmds, '\n') },
189             { V6, Join(v6Cmds, '\n') },
190         };
191     }
192 
193     constexpr static const bool WITH_COUNTERS = true;
194     constexpr static const bool NO_COUNTERS = false;
195     constexpr static const bool WITH_IPV6 = true;
196     constexpr static const bool NO_IPV6 = false;
allNewNatCommands(const char * intIf,const char * extIf,bool withCounterChainRules,bool withIPv6Upstream,bool firstEnableNat)197     ExpectedIptablesCommands allNewNatCommands(const char* intIf, const char* extIf,
198                                                bool withCounterChainRules, bool withIPv6Upstream,
199                                                bool firstEnableNat) {
200         ExpectedIptablesCommands commands;
201         ExpectedIptablesCommands setupFirstIPv4Commands = firstIPv4UpstreamCommands(extIf);
202         ExpectedIptablesCommands startFirstNatCommands = startNatCommands(intIf, extIf,
203             withCounterChainRules);
204 
205         appendAll(commands, setupFirstIPv4Commands);
206         if (withIPv6Upstream) {
207             ExpectedIptablesCommands setupFirstIPv6Commands = firstIPv6UpstreamCommands();
208             appendAll(commands, setupFirstIPv6Commands);
209         }
210         if (firstEnableNat) {
211             appendAll(commands, ALERT_ADD_COMMAND);
212         }
213         appendAll(commands, startFirstNatCommands);
214 
215         return commands;
216     }
217 
stopNatCommands(const char * intIf,const char * extIf)218     ExpectedIptablesCommands stopNatCommands(const char *intIf, const char *extIf) {
219         std::string rpfilterCmd = StringPrintf(
220             "*raw\n"
221             "-D tetherctrl_raw_PREROUTING -i %s -m rpfilter --invert ! -s fe80::/64 -j DROP\n"
222             "COMMIT\n", intIf);
223 
224         std::vector<std::string> v4Cmds = {
225                 "*raw",
226                 StringPrintf(
227                         "-D tetherctrl_raw_PREROUTING -p tcp --dport 21 -i %s -j CT --helper ftp",
228                         intIf),
229                 StringPrintf("-D tetherctrl_raw_PREROUTING -p tcp --dport 1723 -i %s -j CT "
230                              "--helper pptp",
231                              intIf),
232                 "COMMIT",
233                 "*filter",
234                 StringPrintf("-D tetherctrl_FORWARD -i %s -o %s -m state --state"
235                              " ESTABLISHED,RELATED -g tetherctrl_counters",
236                              extIf, intIf),
237                 StringPrintf("-D tetherctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
238                              intIf, extIf),
239                 StringPrintf("-D tetherctrl_FORWARD -i %s -o %s -g tetherctrl_counters", intIf,
240                              extIf),
241                 "COMMIT\n",
242         };
243 
244         return {
245             { V6, rpfilterCmd },
246             { V4, Join(v4Cmds, '\n') },
247         };
248 
249     }
250 };
251 
TEST_F(TetherControllerTest,TestSetupIptablesHooks)252 TEST_F(TetherControllerTest, TestSetupIptablesHooks) {
253     mTetherCtrl.setupIptablesHooks();
254     expectIptablesRestoreCommands(SETUP_COMMANDS);
255 }
256 
TEST_F(TetherControllerTest,TestSetDefaults)257 TEST_F(TetherControllerTest, TestSetDefaults) {
258     setDefaults();
259     expectIptablesRestoreCommands(FLUSH_COMMANDS);
260 }
261 
TEST_F(TetherControllerTest,TestAddAndRemoveNat)262 TEST_F(TetherControllerTest, TestAddAndRemoveNat) {
263     // Start first NAT on first upstream interface. Expect the upstream and NAT rules to be created.
264     ExpectedIptablesCommands firstNat =
265             allNewNatCommands("wlan0", "rmnet0", WITH_COUNTERS, WITH_IPV6, true);
266     mTetherCtrl.enableNat("wlan0", "rmnet0");
267     expectIptablesRestoreCommands(firstNat);
268 
269     // Start second NAT on same upstream. Expect only the counter rules to be created.
270     ExpectedIptablesCommands startOtherNatOnSameUpstream = startNatCommands(
271             "usb0", "rmnet0", WITH_COUNTERS);
272     mTetherCtrl.enableNat("usb0", "rmnet0");
273     expectIptablesRestoreCommands(startOtherNatOnSameUpstream);
274 
275     // Remove the first NAT.
276     ExpectedIptablesCommands stopFirstNat = stopNatCommands("wlan0", "rmnet0");
277     mTetherCtrl.disableNat("wlan0", "rmnet0");
278     expectIptablesRestoreCommands(stopFirstNat);
279 
280     // Remove the last NAT. Expect rules to be cleared.
281     ExpectedIptablesCommands stopLastNat = stopNatCommands("usb0", "rmnet0");
282 
283     appendAll(stopLastNat, FLUSH_COMMANDS);
284     mTetherCtrl.disableNat("usb0", "rmnet0");
285     expectIptablesRestoreCommands(stopLastNat);
286 
287     // Re-add a NAT removed previously: tetherctrl_counters chain rules are not re-added
288     firstNat = allNewNatCommands("wlan0", "rmnet0", NO_COUNTERS, WITH_IPV6, true);
289     mTetherCtrl.enableNat("wlan0", "rmnet0");
290     expectIptablesRestoreCommands(firstNat);
291 
292     // Remove it again. Expect rules to be cleared.
293     stopLastNat = stopNatCommands("wlan0", "rmnet0");
294     appendAll(stopLastNat, FLUSH_COMMANDS);
295     mTetherCtrl.disableNat("wlan0", "rmnet0");
296     expectIptablesRestoreCommands(stopLastNat);
297 }
298 
TEST_F(TetherControllerTest,TestMultipleUpstreams)299 TEST_F(TetherControllerTest, TestMultipleUpstreams) {
300     // Start first NAT on first upstream interface. Expect the upstream and NAT rules to be created.
301     ExpectedIptablesCommands firstNat =
302             allNewNatCommands("wlan0", "rmnet0", WITH_COUNTERS, WITH_IPV6, true);
303     mTetherCtrl.enableNat("wlan0", "rmnet0");
304     expectIptablesRestoreCommands(firstNat);
305 
306     // Start second NAT, on new upstream. Expect the upstream and NAT rules to be created for IPv4,
307     // but no counter rules for IPv6.
308     ExpectedIptablesCommands secondNat =
309             allNewNatCommands("wlan0", "v4-rmnet0", WITH_COUNTERS, NO_IPV6, false);
310     mTetherCtrl.enableNat("wlan0", "v4-rmnet0");
311     expectIptablesRestoreCommands(secondNat);
312 
313     // Pretend that the caller has forgotten that it set up the second NAT, and asks us to do so
314     // again. Expect that we take no action.
315     const ExpectedIptablesCommands NONE = {};
316     mTetherCtrl.enableNat("wlan0", "v4-rmnet0");
317     expectIptablesRestoreCommands(NONE);
318 
319     // Remove the second NAT.
320     ExpectedIptablesCommands stopSecondNat = stopNatCommands("wlan0", "v4-rmnet0");
321     mTetherCtrl.disableNat("wlan0", "v4-rmnet0");
322     expectIptablesRestoreCommands(stopSecondNat);
323 
324     // Remove the first NAT. Expect rules to be cleared.
325     ExpectedIptablesCommands stopFirstNat = stopNatCommands("wlan0", "rmnet0");
326     appendAll(stopFirstNat, FLUSH_COMMANDS);
327     mTetherCtrl.disableNat("wlan0", "rmnet0");
328     expectIptablesRestoreCommands(stopFirstNat);
329 }
330 
331 std::string kTetherCounterHeaders = Join(std::vector<std::string> {
332     "Chain tetherctrl_counters (4 references)",
333     "    pkts      bytes target     prot opt in     out     source               destination",
334 }, '\n');
335 
336 std::string kIPv4TetherCounters = Join(std::vector<std::string> {
337     "Chain tetherctrl_counters (4 references)",
338     "    pkts      bytes target     prot opt in     out     source               destination",
339     "      26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0",
340     "      27     2002 RETURN     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0",
341     "    1040   107471 RETURN     all  --  bt-pan rmnet0  0.0.0.0/0            0.0.0.0/0",
342     "    1450  1708806 RETURN     all  --  rmnet0 bt-pan  0.0.0.0/0            0.0.0.0/0",
343 }, '\n');
344 
345 std::string kIPv6TetherCounters = Join(std::vector<std::string> {
346     "Chain tetherctrl_counters (2 references)",
347     "    pkts      bytes target     prot opt in     out     source               destination",
348     "   10000 10000000 RETURN     all      wlan0  rmnet0  ::/0                 ::/0",
349     "   20000 20000000 RETURN     all      rmnet0 wlan0   ::/0                 ::/0",
350 }, '\n');
351 
expectTetherStatsEqual(const TetherController::TetherStats & expected,const TetherController::TetherStats & actual)352 void expectTetherStatsEqual(const TetherController::TetherStats& expected,
353                             const TetherController::TetherStats& actual) {
354     EXPECT_EQ(expected.intIface, actual.intIface);
355     EXPECT_EQ(expected.extIface, actual.extIface);
356     EXPECT_EQ(expected.rxBytes, actual.rxBytes);
357     EXPECT_EQ(expected.txBytes, actual.txBytes);
358     EXPECT_EQ(expected.rxPackets, actual.rxPackets);
359     EXPECT_EQ(expected.txPackets, actual.txPackets);
360 }
361 
TEST_F(TetherControllerTest,TestGetTetherStats)362 TEST_F(TetherControllerTest, TestGetTetherStats) {
363     // Finding no headers is an error.
364     ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
365     clearIptablesRestoreOutput();
366 
367     // Finding only v4 or only v6 headers is an error.
368     addIptablesRestoreOutput(kTetherCounterHeaders, "");
369     ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
370     clearIptablesRestoreOutput();
371 
372     addIptablesRestoreOutput("", kTetherCounterHeaders);
373     ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
374     clearIptablesRestoreOutput();
375 
376     // Finding headers but no stats is not an error.
377     addIptablesRestoreOutput(kTetherCounterHeaders, kTetherCounterHeaders);
378     StatusOr<TetherStatsList> result = mTetherCtrl.getTetherStats();
379     ASSERT_TRUE(isOk(result));
380     TetherStatsList actual = result.value();
381     ASSERT_EQ(0U, actual.size());
382     clearIptablesRestoreOutput();
383 
384 
385     addIptablesRestoreOutput(kIPv6TetherCounters);
386     ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
387     clearIptablesRestoreOutput();
388 
389     // IPv4 and IPv6 counters are properly added together.
390     addIptablesRestoreOutput(kIPv4TetherCounters, kIPv6TetherCounters);
391     TetherStats expected0("wlan0", "rmnet0", 20002002, 20027, 10002373, 10026);
392     TetherStats expected1("bt-pan", "rmnet0", 1708806, 1450, 107471, 1040);
393     result = mTetherCtrl.getTetherStats();
394     ASSERT_TRUE(isOk(result));
395     actual = result.value();
396     ASSERT_EQ(2U, actual.size());
397     expectTetherStatsEqual(expected0, result.value()[0]);
398     expectTetherStatsEqual(expected1, result.value()[1]);
399     clearIptablesRestoreOutput();
400 
401     // No stats: error.
402     addIptablesRestoreOutput("", kIPv6TetherCounters);
403     ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
404     clearIptablesRestoreOutput();
405 
406     addIptablesRestoreOutput(kIPv4TetherCounters, "");
407     ASSERT_FALSE(isOk(mTetherCtrl.getTetherStats()));
408     clearIptablesRestoreOutput();
409 
410     // Include only one pair of interfaces and things are fine.
411     std::vector<std::string> counterLines = android::base::Split(kIPv4TetherCounters, "\n");
412     std::vector<std::string> brokenCounterLines = counterLines;
413     counterLines.resize(4);
414     std::string counters = Join(counterLines, "\n") + "\n";
415     addIptablesRestoreOutput(counters, counters);
416     TetherStats expected1_0("wlan0", "rmnet0", 4004, 54, 4746, 52);
417     result = mTetherCtrl.getTetherStats();
418     ASSERT_TRUE(isOk(result));
419     actual = result.value();
420     ASSERT_EQ(1U, actual.size());
421     expectTetherStatsEqual(expected1_0, actual[0]);
422     clearIptablesRestoreOutput();
423 
424     // But if interfaces aren't paired, it's always an error.
425     counterLines.resize(3);
426     counters = Join(counterLines, "\n") + "\n";
427     addIptablesRestoreOutput(counters, counters);
428     result = mTetherCtrl.getTetherStats();
429     ASSERT_FALSE(isOk(result));
430     clearIptablesRestoreOutput();
431 
432     // Token unit test of the fact that we return the stats in the error message which the caller
433     // ignores.
434     // Skip header since we only saved the last line we parsed.
435     std::string expectedError = counterLines[2];
436     std::string err = result.status().msg();
437     ASSERT_LE(expectedError.size(), err.size());
438     EXPECT_TRUE(std::equal(expectedError.rbegin(), expectedError.rend(), err.rbegin()));
439 }
440 
441 }  // namespace net
442 }  // namespace android
443