1 /*
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>
18 #include <fcntl.h>
19 #include <sys/file.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22 
23 #include <gtest/gtest.h>
24 
25 #define LOG_TAG "IptablesRestoreControllerTest"
26 #include <cutils/log.h>
27 #include <android-base/stringprintf.h>
28 #include <android-base/strings.h>
29 
30 #include "IptablesRestoreController.h"
31 #include "NetdConstants.h"
32 #include "Stopwatch.h"
33 
34 #define XT_LOCK_NAME "/system/etc/xtables.lock"
35 #define XT_LOCK_ATTEMPTS 10
36 #define XT_LOCK_POLL_INTERVAL_MS 100
37 
38 using android::base::Join;
39 using android::base::StringPrintf;
40 
41 class IptablesRestoreControllerTest : public ::testing::Test {
42 public:
43   IptablesRestoreController con;
44   int mDefaultMaxRetries = con.MAX_RETRIES;
45   int mDefaultPollTimeoutMs = con.POLL_TIMEOUT_MS;
46   int mIptablesLock = -1;
47   std::string mChainName;
48 
SetUpTestCase()49   static void SetUpTestCase() {
50       blockSigpipe();
51   }
52 
SetUp()53   void SetUp() {
54     ASSERT_EQ(0, createTestChain());
55   }
56 
TearDown()57   void TearDown() {
58     con.MAX_RETRIES = mDefaultMaxRetries;
59     con.POLL_TIMEOUT_MS = mDefaultPollTimeoutMs;
60     deleteTestChain();
61   }
62 
getIpRestorePid(const IptablesRestoreController::IptablesProcessType type)63   pid_t getIpRestorePid(const IptablesRestoreController::IptablesProcessType type) {
64       return con.getIpRestorePid(type);
65   };
66 
expectNoIptablesRestoreProcess(pid_t pid)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   }
90 
createTestChain()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 
deleteTestChain()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 
acquireIptablesLock()120   int acquireIptablesLock() {
121     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);
129     }
130     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;
134   }
135 
releaseIptablesLock()136   void releaseIptablesLock() {
137     if (mIptablesLock != -1) {
138       close(mIptablesLock);
139     }
140   }
141 
setRetryParameters(int maxRetries,int pollTimeoutMs)142   void setRetryParameters(int maxRetries, int pollTimeoutMs) {
143     con.MAX_RETRIES = maxRetries;
144     con.POLL_TIMEOUT_MS = pollTimeoutMs;
145   }
146 };
147 
TEST_F(IptablesRestoreControllerTest,TestBasicCommand)148 TEST_F(IptablesRestoreControllerTest, TestBasicCommand) {
149   std::string output;
150 
151   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", nullptr));
152 
153   pid_t pid4 = getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS);
154   pid_t pid6 = getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS);
155 
156   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.
161 
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));
166 }
167 
TEST_F(IptablesRestoreControllerTest,TestRestartOnMalformedCommand)168 TEST_F(IptablesRestoreControllerTest, TestRestartOnMalformedCommand) {
169   std::string buffer;
170   for (int i = 0; i < 50; i++) {
171       IptablesTarget target = (IptablesTarget) (i % 3);
172       std::string *output = (i % 2) ? &buffer : nullptr;
173       ASSERT_EQ(-1, con.execute(target, "malformed command\n", output)) <<
174           "Malformed command did not fail at iteration " << i;
175       ASSERT_EQ(0, con.execute(target, "#Test\n", output)) <<
176           "No-op command did not succeed at iteration " << i;
177   }
178 }
179 
TEST_F(IptablesRestoreControllerTest,TestRestartOnProcessDeath)180 TEST_F(IptablesRestoreControllerTest, TestRestartOnProcessDeath) {
181   std::string output;
182 
183   // Run a command to ensure that the processes are running.
184   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", &output));
185 
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.
198   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", nullptr));
199   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);
205 }
206 
TEST_F(IptablesRestoreControllerTest,TestCommandTimeout)207 TEST_F(IptablesRestoreControllerTest, TestCommandTimeout) {
208   // Don't wait 10 seconds for this test to fail.
209   setRetryParameters(3, 50);
210 
211   // Expected contents of the chain.
212   std::vector<std::string> expectedLines = {
213       StringPrintf("Chain %s (0 references)", mChainName.c_str()),
214       "target     prot opt source               destination         ",
215       "RETURN     all  --  0.0.0.0/0            0.0.0.0/0           ",
216       StringPrintf("Chain %s (0 references)", mChainName.c_str()),
217       "target     prot opt source               destination         ",
218       "RETURN     all      ::/0                 ::/0                ",
219       ""
220   };
221   std::string expected = Join(expectedLines, "\n");
222 
223   std::vector<std::string> listCommands = {
224       "*filter",
225       StringPrintf("-n -L %s", mChainName.c_str()),  // List chain.
226       "COMMIT",
227       ""
228   };
229   std::string commandString = Join(listCommands, "\n");
230   std::string output;
231 
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));
241   EXPECT_EQ(expected, output);
242 }
243 
TEST_F(IptablesRestoreControllerTest,TestUidRuleBenchmark)244 TEST_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 }
273