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