blob: 43041ecf6c22c4c1729fc97cf804d41d9a43880f [file] [log] [blame]
Narayan Kamatha5ace892017-01-06 15:10:02 +00001/*
2 * Copyright 2017 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
17#include <string>
Lorenzo Colitti173da322017-02-05 01:56:40 +090018#include <fcntl.h>
Lorenzo Colitti2bd804a2017-03-10 12:19:08 +090019#include <sys/file.h>
Lorenzo Colittia701afb2017-02-28 01:47:11 +090020#include <sys/socket.h>
21#include <sys/un.h>
Narayan Kamatha5ace892017-01-06 15:10:02 +000022
23#include <gtest/gtest.h>
24
Lorenzo Colitti173da322017-02-05 01:56:40 +090025#define LOG_TAG "IptablesRestoreControllerTest"
26#include <cutils/log.h>
27#include <android-base/stringprintf.h>
Lorenzo Colitticd283772017-01-31 19:00:49 +090028#include <android-base/strings.h>
Lorenzo Colitti173da322017-02-05 01:56:40 +090029
Narayan Kamatha5ace892017-01-06 15:10:02 +000030#include "IptablesRestoreController.h"
31#include "NetdConstants.h"
Lorenzo Colittia7357652017-04-25 00:16:36 +090032#include "Stopwatch.h"
Narayan Kamatha5ace892017-01-06 15:10:02 +000033
Lorenzo Colitti2bd804a2017-03-10 12:19:08 +090034#define XT_LOCK_NAME "/system/etc/xtables.lock"
35#define XT_LOCK_ATTEMPTS 10
36#define XT_LOCK_POLL_INTERVAL_MS 100
Lorenzo Colittia701afb2017-02-28 01:47:11 +090037
Lorenzo Colitticd283772017-01-31 19:00:49 +090038using android::base::Join;
Lorenzo Colitti173da322017-02-05 01:56:40 +090039using android::base::StringPrintf;
Narayan Kamatha5ace892017-01-06 15:10:02 +000040
Lorenzo Colitti173da322017-02-05 01:56:40 +090041class IptablesRestoreControllerTest : public ::testing::Test {
42public:
43 IptablesRestoreController con;
Lorenzo Colittia701afb2017-02-28 01:47:11 +090044 int mDefaultMaxRetries = con.MAX_RETRIES;
45 int mDefaultPollTimeoutMs = con.POLL_TIMEOUT_MS;
46 int mIptablesLock = -1;
47 std::string mChainName;
48
Lorenzo Colitti839d7d62017-04-03 15:37:19 +090049 static void SetUpTestCase() {
50 blockSigpipe();
51 }
52
Lorenzo Colittia701afb2017-02-28 01:47:11 +090053 void SetUp() {
54 ASSERT_EQ(0, createTestChain());
55 }
56
57 void TearDown() {
58 con.MAX_RETRIES = mDefaultMaxRetries;
59 con.POLL_TIMEOUT_MS = mDefaultPollTimeoutMs;
60 deleteTestChain();
61 }
Lorenzo Colitti173da322017-02-05 01:56:40 +090062
63 pid_t getIpRestorePid(const IptablesRestoreController::IptablesProcessType type) {
64 return con.getIpRestorePid(type);
65 };
66
67 void expectNoIptablesRestoreProcess(pid_t pid) {
68 // We can't readlink /proc/PID/exe, because zombie processes don't have it.
69 // Parse /proc/PID/stat instead.
70 std::string statPath = StringPrintf("/proc/%d/stat", pid);
71 int fd = open(statPath.c_str(), O_RDONLY);
72 if (fd == -1) {
73 // ENOENT means the process is gone (expected).
74 ASSERT_EQ(errno, ENOENT)
75 << "Unexpected error opening " << statPath << ": " << strerror(errno);
76 return;
77 }
78
79 // If the PID exists, it's possible (though very unlikely) that the PID was reused. Check the
80 // binary name as well, to ensure the test isn't flaky.
81 char statBuf[1024];
82 ASSERT_NE(-1, read(fd, statBuf, sizeof(statBuf)))
83 << "Could not read from " << statPath << ": " << strerror(errno);
84 close(fd);
85
86 std::string statString(statBuf);
87 EXPECT_FALSE(statString.find("iptables-restor") || statString.find("ip6tables-resto"))
88 << "Previous iptables-restore pid " << pid << " still alive: " << statString;
89 }
Lorenzo Colittia701afb2017-02-28 01:47:11 +090090
91 int createTestChain() {
92 mChainName = StringPrintf("netd_unit_test_%u", arc4random_uniform(10000)).c_str();
93
94 // Create a chain to list.
95 std::vector<std::string> createCommands = {
96 "*filter",
97 StringPrintf(":%s -", mChainName.c_str()),
98 StringPrintf("-A %s -j RETURN", mChainName.c_str()),
99 "COMMIT",
100 ""
101 };
102
103 int ret = con.execute(V4V6, Join(createCommands, "\n"), nullptr);
104 if (ret) mChainName = "";
105 return ret;
106 }
107
108 void deleteTestChain() {
109 std::vector<std::string> deleteCommands = {
110 "*filter",
111 StringPrintf(":%s -", mChainName.c_str()), // Flush chain (otherwise we can't delete it).
112 StringPrintf("-X %s", mChainName.c_str()), // Delete it.
113 "COMMIT",
114 ""
115 };
116 con.execute(V4V6, Join(deleteCommands, "\n"), nullptr);
117 mChainName = "";
118 }
119
120 int acquireIptablesLock() {
Lorenzo Colitti2bd804a2017-03-10 12:19:08 +0900121 mIptablesLock = open(XT_LOCK_NAME, O_CREAT, 0600);
122 if (mIptablesLock == -1) return mIptablesLock;
123 int attempts;
124 for (attempts = 0; attempts < XT_LOCK_ATTEMPTS; attempts++) {
125 if (flock(mIptablesLock, LOCK_EX | LOCK_NB) == 0) {
126 return 0;
127 }
128 usleep(XT_LOCK_POLL_INTERVAL_MS * 1000);
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900129 }
Lorenzo Colitti2bd804a2017-03-10 12:19:08 +0900130 EXPECT_LT(attempts, XT_LOCK_ATTEMPTS) <<
131 "Could not acquire iptables lock after " << XT_LOCK_ATTEMPTS << " attempts " <<
132 XT_LOCK_POLL_INTERVAL_MS << "ms apart";
133 return -1;
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900134 }
135
136 void releaseIptablesLock() {
137 if (mIptablesLock != -1) {
138 close(mIptablesLock);
139 }
140 }
141
142 void setRetryParameters(int maxRetries, int pollTimeoutMs) {
143 con.MAX_RETRIES = maxRetries;
144 con.POLL_TIMEOUT_MS = pollTimeoutMs;
145 }
Narayan Kamatha5ace892017-01-06 15:10:02 +0000146};
147
Lorenzo Colitti173da322017-02-05 01:56:40 +0900148TEST_F(IptablesRestoreControllerTest, TestBasicCommand) {
Lorenzo Colitticd283772017-01-31 19:00:49 +0900149 std::string output;
150
151 EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", nullptr));
Lorenzo Colitti173da322017-02-05 01:56:40 +0900152
153 pid_t pid4 = getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS);
154 pid_t pid6 = getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS);
155
Lorenzo Colitticd283772017-01-31 19:00:49 +0900156 EXPECT_EQ(0, con.execute(IptablesTarget::V6, "#Test\n", nullptr));
157 EXPECT_EQ(0, con.execute(IptablesTarget::V4, "#Test\n", nullptr));
158
159 EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", &output));
160 EXPECT_EQ("#Test\n#Test\n", output); // One for IPv4 and one for IPv6.
Lorenzo Colitti173da322017-02-05 01:56:40 +0900161
162 // Check the PIDs are the same as they were before. If they're not, the child processes were
163 // restarted, which causes a 30-60ms delay.
164 EXPECT_EQ(pid4, getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS));
165 EXPECT_EQ(pid6, getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS));
Narayan Kamatha5ace892017-01-06 15:10:02 +0000166}
167
Lorenzo Colitti173da322017-02-05 01:56:40 +0900168TEST_F(IptablesRestoreControllerTest, TestRestartOnMalformedCommand) {
Lorenzo Colitticd283772017-01-31 19:00:49 +0900169 std::string buffer;
Lorenzo Colitti173da322017-02-05 01:56:40 +0900170 for (int i = 0; i < 50; i++) {
171 IptablesTarget target = (IptablesTarget) (i % 3);
Lorenzo Colitticd283772017-01-31 19:00:49 +0900172 std::string *output = (i % 2) ? &buffer : nullptr;
173 ASSERT_EQ(-1, con.execute(target, "malformed command\n", output)) <<
Lorenzo Colitti173da322017-02-05 01:56:40 +0900174 "Malformed command did not fail at iteration " << i;
Lorenzo Colitticd283772017-01-31 19:00:49 +0900175 ASSERT_EQ(0, con.execute(target, "#Test\n", output)) <<
Lorenzo Colitti173da322017-02-05 01:56:40 +0900176 "No-op command did not succeed at iteration " << i;
177 }
178}
179
180TEST_F(IptablesRestoreControllerTest, TestRestartOnProcessDeath) {
Lorenzo Colitticd283772017-01-31 19:00:49 +0900181 std::string output;
182
Lorenzo Colitti173da322017-02-05 01:56:40 +0900183 // Run a command to ensure that the processes are running.
Lorenzo Colitticd283772017-01-31 19:00:49 +0900184 EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", &output));
Lorenzo Colitti173da322017-02-05 01:56:40 +0900185
186 pid_t pid4 = getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS);
187 pid_t pid6 = getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS);
188
189 ASSERT_EQ(0, kill(pid4, 0)) << "iptables-restore pid " << pid4 << " does not exist";
190 ASSERT_EQ(0, kill(pid6, 0)) << "ip6tables-restore pid " << pid6 << " does not exist";
191 ASSERT_EQ(0, kill(pid4, SIGTERM)) << "Failed to send SIGTERM to iptables-restore pid " << pid4;
192 ASSERT_EQ(0, kill(pid6, SIGTERM)) << "Failed to send SIGTERM to ip6tables-restore pid " << pid6;
193
194 // Wait 100ms for processes to terminate.
195 TEMP_FAILURE_RETRY(usleep(100 * 1000));
196
197 // Ensure that running a new command properly restarts the processes.
Lorenzo Colitticd283772017-01-31 19:00:49 +0900198 EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", nullptr));
Lorenzo Colitti173da322017-02-05 01:56:40 +0900199 EXPECT_NE(pid4, getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS));
200 EXPECT_NE(pid6, getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS));
201
202 // Check there are no zombies.
203 expectNoIptablesRestoreProcess(pid4);
204 expectNoIptablesRestoreProcess(pid6);
Narayan Kamatha5ace892017-01-06 15:10:02 +0000205}
Lorenzo Colitticd283772017-01-31 19:00:49 +0900206
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900207TEST_F(IptablesRestoreControllerTest, TestCommandTimeout) {
208 // Don't wait 10 seconds for this test to fail.
209 setRetryParameters(3, 50);
Lorenzo Colitticd283772017-01-31 19:00:49 +0900210
211 // Expected contents of the chain.
212 std::vector<std::string> expectedLines = {
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900213 StringPrintf("Chain %s (0 references)", mChainName.c_str()),
Lorenzo Colitticd283772017-01-31 19:00:49 +0900214 "target prot opt source destination ",
215 "RETURN all -- 0.0.0.0/0 0.0.0.0/0 ",
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900216 StringPrintf("Chain %s (0 references)", mChainName.c_str()),
Lorenzo Colitticd283772017-01-31 19:00:49 +0900217 "target prot opt source destination ",
218 "RETURN all ::/0 ::/0 ",
219 ""
220 };
221 std::string expected = Join(expectedLines, "\n");
222
Lorenzo Colitticd283772017-01-31 19:00:49 +0900223 std::vector<std::string> listCommands = {
224 "*filter",
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900225 StringPrintf("-n -L %s", mChainName.c_str()), // List chain.
Lorenzo Colitticd283772017-01-31 19:00:49 +0900226 "COMMIT",
227 ""
228 };
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900229 std::string commandString = Join(listCommands, "\n");
Lorenzo Colitticd283772017-01-31 19:00:49 +0900230 std::string output;
Lorenzo Colittia701afb2017-02-28 01:47:11 +0900231
232 EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, commandString, &output));
233 EXPECT_EQ(expected, output);
234
235 ASSERT_EQ(0, acquireIptablesLock());
236 EXPECT_EQ(-1, con.execute(IptablesTarget::V4V6, commandString, &output));
237 EXPECT_EQ(-1, con.execute(IptablesTarget::V4V6, commandString, &output));
238 releaseIptablesLock();
239
240 EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, commandString, &output));
Lorenzo Colitticd283772017-01-31 19:00:49 +0900241 EXPECT_EQ(expected, output);
242}
Lorenzo Colittia7357652017-04-25 00:16:36 +0900243
244TEST_F(IptablesRestoreControllerTest, TestUidRuleBenchmark) {
245 const std::vector<int> ITERATIONS = { 1, 5, 10 };
246
247 const std::string IPTABLES_RESTORE_ADD =
248 "*filter\n-I fw_powersave -m owner --uid-owner 2000000000 -j RETURN\nCOMMIT\n";
249 const std::string IPTABLES_RESTORE_DEL =
250 "*filter\n-D fw_powersave -m owner --uid-owner 2000000000 -j RETURN\nCOMMIT\n";
251
252 for (const int iterations : ITERATIONS) {
253 Stopwatch s;
254 for (int i = 0; i < iterations; i++) {
255 EXPECT_EQ(0, con.execute(V4V6, IPTABLES_RESTORE_ADD, nullptr));
256 EXPECT_EQ(0, con.execute(V4V6, IPTABLES_RESTORE_DEL, nullptr));
257 }
258 float timeTaken = s.getTimeAndReset();
259 fprintf(stderr, " Add/del %d UID rules via restore: %.1fms (%.2fms per operation)\n",
260 iterations, timeTaken, timeTaken / 2 / iterations);
261
262 for (int i = 0; i < iterations; i++) {
263 EXPECT_EQ(0, execIptables(V4V6, "-I", "fw_powersave", "-m", "owner",
264 "--uid-owner", "2000000000", "-j", "RETURN", nullptr));
265 EXPECT_EQ(0, execIptables(V4V6, "-D", "fw_powersave", "-m", "owner",
266 "--uid-owner", "2000000000", "-j", "RETURN", nullptr));
267 }
268 timeTaken = s.getTimeAndReset();
269 fprintf(stderr, " Add/del %d UID rules via iptables: %.1fms (%.2fms per operation)\n",
270 iterations, timeTaken, timeTaken / 2 / iterations);
271 }
272}