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