summaryrefslogtreecommitdiff
path: root/server/TetherControllerTest.cpp
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2017-08-24 13:28:19 +0900
committerLorenzo Colitti <lorenzo@google.com>2017-08-31 16:24:31 +0900
commita93126d6a1fb762537916adf2f103f893689e50f (patch)
tree7a80f6e4726f5bcd36d62cf16e861b9c993b2920 /server/TetherControllerTest.cpp
parente2e18f0400f9b5ef5a1329cbdddfb447d72910f5 (diff)
Move all tethering functionality into TetherController.
This includes the entirety of NatController and relevant parts of BandwidthController (specifically, getTetherStats and its dependencies). This will make the code easier to understand and allow netd to privide a simpler API to the framework (e.g., by providing higher-level methods that perform what today are multiple operations from the framework's point of view). It will also reduce duplication of state (e.g., interface pairs kept by NatController partially overlap with downstream interfaces kept by TetherController) and avoid dependencies between controllers. This CL makes no functional changes. The only code changes are the ones necessary for compilation. Specifically: - Move some methods into IptablesBaseTest.cpp so they can be used from two test classes. - Change TetherController::iptablesRestoreFunction to the three-argument used by the erstwhile BandwidthController tests. Where appropriate, variable names have been changed, but strings that actually appear on device, such as iptables rule names, are left as is and will be changed in a future CL. Bug: 32163131 Bug: 64995262 Test: bullhead builds, boots Test: netd_{unit,integration}_test pass Change-Id: Idc80bfd424ce011826305f84b1cc98e741698601
Diffstat (limited to 'server/TetherControllerTest.cpp')
-rw-r--r--server/TetherControllerTest.cpp351
1 files changed, 351 insertions, 0 deletions
diff --git a/server/TetherControllerTest.cpp b/server/TetherControllerTest.cpp
new file mode 100644
index 00000000..887c6488
--- /dev/null
+++ b/server/TetherControllerTest.cpp
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * TetherControllerTest.cpp - unit tests for TetherController.cpp
+ */
+
+#include <string>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "TetherController.h"
+#include "IptablesBaseTest.h"
+
+using android::base::Join;
+using android::base::StringPrintf;
+
+namespace android {
+namespace net {
+
+class TetherControllerTest : public IptablesBaseTest {
+public:
+ TetherControllerTest() {
+ TetherController::iptablesRestoreFunction = fakeExecIptablesRestoreWithOutput;
+ }
+
+protected:
+ TetherController mTetherCtrl;
+
+ int setDefaults() {
+ return mTetherCtrl.setDefaults();
+ }
+
+ const ExpectedIptablesCommands FLUSH_COMMANDS = {
+ { V4, "*filter\n"
+ ":natctrl_FORWARD -\n"
+ "-A natctrl_FORWARD -j DROP\n"
+ "COMMIT\n"
+ "*nat\n"
+ ":natctrl_nat_POSTROUTING -\n"
+ "COMMIT\n" },
+ { V6, "*filter\n"
+ ":natctrl_FORWARD -\n"
+ "COMMIT\n"
+ "*raw\n"
+ ":natctrl_raw_PREROUTING -\n"
+ "COMMIT\n" },
+ };
+
+ const ExpectedIptablesCommands SETUP_COMMANDS = {
+ { V4, "*filter\n"
+ ":natctrl_FORWARD -\n"
+ "-A natctrl_FORWARD -j DROP\n"
+ "COMMIT\n"
+ "*nat\n"
+ ":natctrl_nat_POSTROUTING -\n"
+ "COMMIT\n" },
+ { V6, "*filter\n"
+ ":natctrl_FORWARD -\n"
+ "COMMIT\n"
+ "*raw\n"
+ ":natctrl_raw_PREROUTING -\n"
+ "COMMIT\n" },
+ { V4, "*mangle\n"
+ "-A natctrl_mangle_FORWARD -p tcp --tcp-flags SYN SYN "
+ "-j TCPMSS --clamp-mss-to-pmtu\n"
+ "COMMIT\n" },
+ { V4V6, "*filter\n"
+ ":natctrl_tether_counters -\n"
+ "COMMIT\n" },
+ };
+
+ ExpectedIptablesCommands firstNatCommands(const char *extIf) {
+ std::string v4Cmd = StringPrintf(
+ "*nat\n"
+ "-A natctrl_nat_POSTROUTING -o %s -j MASQUERADE\n"
+ "COMMIT\n", extIf);
+ std::string v6Cmd =
+ "*filter\n"
+ "-A natctrl_FORWARD -g natctrl_tether_counters\n"
+ "COMMIT\n";
+ return {
+ { V4, v4Cmd },
+ { V6, v6Cmd },
+ };
+ }
+
+ ExpectedIptablesCommands startNatCommands(const char *intIf, const char *extIf) {
+ std::string rpfilterCmd = StringPrintf(
+ "*raw\n"
+ "-A natctrl_raw_PREROUTING -i %s -m rpfilter --invert ! -s fe80::/64 -j DROP\n"
+ "COMMIT\n", intIf);
+
+ std::vector<std::string> v4Cmds = {
+ "*filter",
+ StringPrintf("-A natctrl_FORWARD -i %s -o %s -m state --state"
+ " ESTABLISHED,RELATED -g natctrl_tether_counters", extIf, intIf),
+ StringPrintf("-A natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
+ intIf, extIf),
+ StringPrintf("-A natctrl_FORWARD -i %s -o %s -g natctrl_tether_counters",
+ intIf, extIf),
+ StringPrintf("-A natctrl_tether_counters -i %s -o %s -j RETURN", intIf, extIf),
+ StringPrintf("-A natctrl_tether_counters -i %s -o %s -j RETURN", extIf, intIf),
+ "-D natctrl_FORWARD -j DROP",
+ "-A natctrl_FORWARD -j DROP",
+ "COMMIT\n",
+ };
+
+ std::vector<std::string> v6Cmds = {
+ "*filter",
+ StringPrintf("-A natctrl_tether_counters -i %s -o %s -j RETURN", intIf, extIf),
+ StringPrintf("-A natctrl_tether_counters -i %s -o %s -j RETURN", extIf, intIf),
+ "COMMIT\n",
+ };
+
+ return {
+ { V6, rpfilterCmd },
+ { V4, Join(v4Cmds, '\n') },
+ { V6, Join(v6Cmds, '\n') },
+ };
+ }
+
+ ExpectedIptablesCommands stopNatCommands(const char *intIf, const char *extIf) {
+ std::string rpfilterCmd = StringPrintf(
+ "*raw\n"
+ "-D natctrl_raw_PREROUTING -i %s -m rpfilter --invert ! -s fe80::/64 -j DROP\n"
+ "COMMIT\n", intIf);
+
+ std::vector<std::string> v4Cmds = {
+ "*filter",
+ StringPrintf("-D natctrl_FORWARD -i %s -o %s -m state --state"
+ " ESTABLISHED,RELATED -g natctrl_tether_counters", extIf, intIf),
+ StringPrintf("-D natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
+ intIf, extIf),
+ StringPrintf("-D natctrl_FORWARD -i %s -o %s -g natctrl_tether_counters",
+ intIf, extIf),
+ "COMMIT\n",
+ };
+
+ return {
+ { V6, rpfilterCmd },
+ { V4, Join(v4Cmds, '\n') },
+ };
+
+ }
+};
+
+TEST_F(TetherControllerTest, TestSetupIptablesHooks) {
+ mTetherCtrl.setupIptablesHooks();
+ expectIptablesRestoreCommands(SETUP_COMMANDS);
+}
+
+TEST_F(TetherControllerTest, TestSetDefaults) {
+ setDefaults();
+ expectIptablesRestoreCommands(FLUSH_COMMANDS);
+}
+
+TEST_F(TetherControllerTest, TestAddAndRemoveNat) {
+ ExpectedIptablesCommands expected;
+ ExpectedIptablesCommands setupFirstNatCommands = firstNatCommands("rmnet0");
+ ExpectedIptablesCommands startFirstNatCommands = startNatCommands("wlan0", "rmnet0");
+ expected.insert(expected.end(), setupFirstNatCommands.begin(), setupFirstNatCommands.end());
+ expected.insert(expected.end(), startFirstNatCommands.begin(), startFirstNatCommands.end());
+ mTetherCtrl.enableNat("wlan0", "rmnet0");
+ expectIptablesRestoreCommands(expected);
+
+ ExpectedIptablesCommands startOtherNat = startNatCommands("usb0", "rmnet0");
+ mTetherCtrl.enableNat("usb0", "rmnet0");
+ expectIptablesRestoreCommands(startOtherNat);
+
+ ExpectedIptablesCommands stopOtherNat = stopNatCommands("wlan0", "rmnet0");
+ mTetherCtrl.disableNat("wlan0", "rmnet0");
+ expectIptablesRestoreCommands(stopOtherNat);
+
+ expected = stopNatCommands("usb0", "rmnet0");
+ expected.insert(expected.end(), FLUSH_COMMANDS.begin(), FLUSH_COMMANDS.end());
+ mTetherCtrl.disableNat("usb0", "rmnet0");
+ expectIptablesRestoreCommands(expected);
+}
+
+std::string kIPv4TetherCounters = Join(std::vector<std::string> {
+ "Chain natctrl_tether_counters (4 references)",
+ " pkts bytes target prot opt in out source destination",
+ " 26 2373 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0",
+ " 27 2002 RETURN all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0",
+ " 1040 107471 RETURN all -- bt-pan rmnet0 0.0.0.0/0 0.0.0.0/0",
+ " 1450 1708806 RETURN all -- rmnet0 bt-pan 0.0.0.0/0 0.0.0.0/0",
+}, '\n');
+
+std::string kIPv6TetherCounters = Join(std::vector<std::string> {
+ "Chain natctrl_tether_counters (2 references)",
+ " pkts bytes target prot opt in out source destination",
+ " 10000 10000000 RETURN all wlan0 rmnet0 ::/0 ::/0",
+ " 20000 20000000 RETURN all rmnet0 wlan0 ::/0 ::/0",
+}, '\n');
+
+std::string readSocketClientResponse(int fd) {
+ char buf[32768];
+ ssize_t bytesRead = read(fd, buf, sizeof(buf));
+ if (bytesRead < 0) {
+ return "";
+ }
+ for (int i = 0; i < bytesRead; i++) {
+ if (buf[i] == '\0') buf[i] = '\n';
+ }
+ return std::string(buf, bytesRead);
+}
+
+void expectNoSocketClientResponse(int fd) {
+ char buf[64];
+ EXPECT_EQ(-1, read(fd, buf, sizeof(buf)));
+}
+
+TEST_F(TetherControllerTest, TestGetTetherStats) {
+ int socketPair[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair));
+ ASSERT_EQ(0, fcntl(socketPair[0], F_SETFL, O_NONBLOCK | fcntl(socketPair[0], F_GETFL)));
+ ASSERT_EQ(0, fcntl(socketPair[1], F_SETFL, O_NONBLOCK | fcntl(socketPair[1], F_GETFL)));
+ SocketClient cli(socketPair[0], false);
+
+ std::string err;
+ TetherController::TetherStats filter;
+
+ // If no filter is specified, both IPv4 and IPv6 counters must have at least one interface pair.
+ addIptablesRestoreOutput(kIPv4TetherCounters);
+ ASSERT_EQ(-1, mTetherCtrl.getTetherStats(&cli, filter, err));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+
+ addIptablesRestoreOutput(kIPv6TetherCounters);
+ ASSERT_EQ(-1, mTetherCtrl.getTetherStats(&cli, filter, err));
+ clearIptablesRestoreOutput();
+
+ // IPv4 and IPv6 counters are properly added together.
+ addIptablesRestoreOutput(kIPv4TetherCounters, kIPv6TetherCounters);
+ filter = TetherController::TetherStats();
+ std::string expected =
+ "114 wlan0 rmnet0 10002373 10026 20002002 20027\n"
+ "114 bt-pan rmnet0 107471 1040 1708806 1450\n"
+ "200 Tethering stats list completed\n";
+ ASSERT_EQ(0, mTetherCtrl.getTetherStats(&cli, filter, err));
+ ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+
+ // Test filtering.
+ addIptablesRestoreOutput(kIPv4TetherCounters, kIPv6TetherCounters);
+ filter = TetherController::TetherStats("bt-pan", "rmnet0", -1, -1, -1, -1);
+ expected = "221 bt-pan rmnet0 107471 1040 1708806 1450\n";
+ ASSERT_EQ(0, mTetherCtrl.getTetherStats(&cli, filter, err));
+ ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+
+ addIptablesRestoreOutput(kIPv4TetherCounters, kIPv6TetherCounters);
+ filter = TetherController::TetherStats("wlan0", "rmnet0", -1, -1, -1, -1);
+ expected = "221 wlan0 rmnet0 10002373 10026 20002002 20027\n";
+ ASSERT_EQ(0, mTetherCtrl.getTetherStats(&cli, filter, err));
+ ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+ clearIptablesRestoreOutput();
+
+ // Select nonexistent interfaces.
+ addIptablesRestoreOutput(kIPv4TetherCounters, kIPv6TetherCounters);
+ filter = TetherController::TetherStats("rmnet0", "foo0", -1, -1, -1, -1);
+ expected = "200 Tethering stats list completed\n";
+ ASSERT_EQ(0, mTetherCtrl.getTetherStats(&cli, filter, err));
+ ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+ clearIptablesRestoreOutput();
+
+ // No stats with a filter: no error.
+ addIptablesRestoreOutput("", "");
+ ASSERT_EQ(0, mTetherCtrl.getTetherStats(&cli, filter, err));
+ ASSERT_EQ("200 Tethering stats list completed\n", readSocketClientResponse(socketPair[1]));
+ clearIptablesRestoreOutput();
+
+ addIptablesRestoreOutput("foo", "foo");
+ ASSERT_EQ(0, mTetherCtrl.getTetherStats(&cli, filter, err));
+ ASSERT_EQ("200 Tethering stats list completed\n", readSocketClientResponse(socketPair[1]));
+ clearIptablesRestoreOutput();
+
+ // No stats and empty filter: error.
+ filter = TetherController::TetherStats();
+ addIptablesRestoreOutput("", kIPv6TetherCounters);
+ ASSERT_EQ(-1, mTetherCtrl.getTetherStats(&cli, filter, err));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+
+ addIptablesRestoreOutput(kIPv4TetherCounters, "");
+ ASSERT_EQ(-1, mTetherCtrl.getTetherStats(&cli, filter, err));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+
+ // Include only one pair of interfaces and things are fine.
+ std::vector<std::string> counterLines = android::base::Split(kIPv4TetherCounters, "\n");
+ std::vector<std::string> brokenCounterLines = counterLines;
+ counterLines.resize(4);
+ std::string counters = Join(counterLines, "\n") + "\n";
+ addIptablesRestoreOutput(counters, counters);
+ expected =
+ "114 wlan0 rmnet0 4746 52 4004 54\n"
+ "200 Tethering stats list completed\n";
+ ASSERT_EQ(0, mTetherCtrl.getTetherStats(&cli, filter, err));
+ ASSERT_EQ(expected, readSocketClientResponse(socketPair[1]));
+ clearIptablesRestoreOutput();
+
+ // But if interfaces aren't paired, it's always an error.
+ err = "";
+ counterLines.resize(3);
+ counters = Join(counterLines, "\n") + "\n";
+ addIptablesRestoreOutput(counters, counters);
+ ASSERT_EQ(-1, mTetherCtrl.getTetherStats(&cli, filter, err));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+
+ // Token unit test of the fact that we return the stats in the error message which the caller
+ // ignores.
+ std::string expectedError = counters;
+ EXPECT_EQ(expectedError, err);
+
+ addIptablesRestoreOutput(kIPv4TetherCounters);
+ ASSERT_EQ(-1, mTetherCtrl.getTetherStats(&cli, filter, err));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+ addIptablesRestoreOutput(kIPv6TetherCounters);
+ ASSERT_EQ(-1, mTetherCtrl.getTetherStats(&cli, filter, err));
+ expectNoSocketClientResponse(socketPair[1]);
+ clearIptablesRestoreOutput();
+}
+
+} // namespace net
+} // namespace android