• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <gmock/gmock.h>
24 #include <gtest/gtest.h>
25 
26 #define LOG_TAG "IptablesRestoreControllerTest"
27 #include <cutils/log.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <netdutils/MockSyscalls.h>
31 
32 #include "IptablesRestoreController.h"
33 #include "NetdConstants.h"
34 #include "Stopwatch.h"
35 #include "bpf/BpfUtils.h"
36 
37 #define XT_LOCK_NAME "/system/etc/xtables.lock"
38 #define XT_LOCK_ATTEMPTS 10
39 #define XT_LOCK_POLL_INTERVAL_MS 100
40 
41 using android::base::Join;
42 using android::base::StringPrintf;
43 using android::bpf::DOZABLE_UID_MAP_PATH;
44 using android::bpf::STANDBY_UID_MAP_PATH;
45 using android::bpf::POWERSAVE_UID_MAP_PATH;
46 using android::netdutils::ScopedMockSyscalls;
47 using testing::Return;
48 using testing::StrictMock;
49 
50 class IptablesRestoreControllerTest : public ::testing::Test {
51 public:
52   IptablesRestoreController con;
53   int mDefaultMaxRetries = con.MAX_RETRIES;
54   int mDefaultPollTimeoutMs = con.POLL_TIMEOUT_MS;
55   int mIptablesLock = -1;
56   std::string mChainName;
57 
SetUpTestCase()58   static void SetUpTestCase() {
59       blockSigpipe();
60   }
61 
SetUp()62   void SetUp() {
63     ASSERT_EQ(0, createTestChain());
64   }
65 
TearDown()66   void TearDown() {
67     con.MAX_RETRIES = mDefaultMaxRetries;
68     con.POLL_TIMEOUT_MS = mDefaultPollTimeoutMs;
69     deleteTestChain();
70   }
71 
Init()72   void Init() {
73     con.Init();
74   }
75 
getIpRestorePid(const IptablesRestoreController::IptablesProcessType type)76   pid_t getIpRestorePid(const IptablesRestoreController::IptablesProcessType type) {
77       return con.getIpRestorePid(type);
78   };
79 
expectNoIptablesRestoreProcess(pid_t pid)80   void expectNoIptablesRestoreProcess(pid_t pid) {
81     // We can't readlink /proc/PID/exe, because zombie processes don't have it.
82     // Parse /proc/PID/stat instead.
83     std::string statPath = StringPrintf("/proc/%d/stat", pid);
84     int fd = open(statPath.c_str(), O_RDONLY);
85     if (fd == -1) {
86       // ENOENT means the process is gone (expected).
87       ASSERT_EQ(errno, ENOENT)
88         << "Unexpected error opening " << statPath << ": " << strerror(errno);
89       return;
90     }
91 
92     // If the PID exists, it's possible (though very unlikely) that the PID was reused. Check the
93     // binary name as well, to ensure the test isn't flaky.
94     char statBuf[1024];
95     ASSERT_NE(-1, read(fd, statBuf, sizeof(statBuf)))
96         << "Could not read from " << statPath << ": " << strerror(errno);
97     close(fd);
98 
99     std::string statString(statBuf);
100     EXPECT_FALSE(statString.find("iptables-restor") || statString.find("ip6tables-resto"))
101       << "Previous iptables-restore pid " << pid << " still alive: " << statString;
102   }
103 
createTestChain()104   int createTestChain() {
105     mChainName = StringPrintf("netd_unit_test_%u", arc4random_uniform(10000)).c_str();
106 
107     // Create a chain to list.
108     std::vector<std::string> createCommands = {
109         "*filter",
110         StringPrintf(":%s -", mChainName.c_str()),
111         StringPrintf("-A %s -j RETURN", mChainName.c_str()),
112         "COMMIT",
113         ""
114     };
115 
116     int ret = con.execute(V4V6, Join(createCommands, "\n"), nullptr);
117     if (ret) mChainName = "";
118     return ret;
119   }
120 
deleteTestChain()121   void deleteTestChain() {
122     std::vector<std::string> deleteCommands = {
123         "*filter",
124         StringPrintf(":%s -", mChainName.c_str()),  // Flush chain (otherwise we can't delete it).
125         StringPrintf("-X %s", mChainName.c_str()),  // Delete it.
126         "COMMIT",
127         ""
128     };
129     con.execute(V4V6, Join(deleteCommands, "\n"), nullptr);
130     mChainName = "";
131   }
132 
acquireIptablesLock()133   int acquireIptablesLock() {
134     mIptablesLock = open(XT_LOCK_NAME, O_CREAT, 0600);
135     if (mIptablesLock == -1) return mIptablesLock;
136     int attempts;
137     for (attempts = 0; attempts < XT_LOCK_ATTEMPTS; attempts++) {
138       if (flock(mIptablesLock, LOCK_EX | LOCK_NB) == 0) {
139         return 0;
140       }
141       usleep(XT_LOCK_POLL_INTERVAL_MS * 1000);
142     }
143     EXPECT_LT(attempts, XT_LOCK_ATTEMPTS) <<
144         "Could not acquire iptables lock after " << XT_LOCK_ATTEMPTS << " attempts " <<
145         XT_LOCK_POLL_INTERVAL_MS << "ms apart";
146     return -1;
147   }
148 
releaseIptablesLock()149   void releaseIptablesLock() {
150     if (mIptablesLock != -1) {
151       close(mIptablesLock);
152     }
153   }
154 
setRetryParameters(int maxRetries,int pollTimeoutMs)155   void setRetryParameters(int maxRetries, int pollTimeoutMs) {
156     con.MAX_RETRIES = maxRetries;
157     con.POLL_TIMEOUT_MS = pollTimeoutMs;
158   }
159 };
160 
TEST_F(IptablesRestoreControllerTest,TestBasicCommand)161 TEST_F(IptablesRestoreControllerTest, TestBasicCommand) {
162   std::string output;
163 
164   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", nullptr));
165 
166   pid_t pid4 = getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS);
167   pid_t pid6 = getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS);
168 
169   EXPECT_EQ(0, con.execute(IptablesTarget::V6, "#Test\n", nullptr));
170   EXPECT_EQ(0, con.execute(IptablesTarget::V4, "#Test\n", nullptr));
171 
172   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", &output));
173   EXPECT_EQ("#Test\n#Test\n", output);  // One for IPv4 and one for IPv6.
174 
175   // Check the PIDs are the same as they were before. If they're not, the child processes were
176   // restarted, which causes a 30-60ms delay.
177   EXPECT_EQ(pid4, getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS));
178   EXPECT_EQ(pid6, getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS));
179 }
180 
TEST_F(IptablesRestoreControllerTest,TestRestartOnMalformedCommand)181 TEST_F(IptablesRestoreControllerTest, TestRestartOnMalformedCommand) {
182   std::string buffer;
183   for (int i = 0; i < 50; i++) {
184       IptablesTarget target = (IptablesTarget) (i % 3);
185       std::string *output = (i % 2) ? &buffer : nullptr;
186       ASSERT_EQ(-1, con.execute(target, "malformed command\n", output)) <<
187           "Malformed command did not fail at iteration " << i;
188       ASSERT_EQ(0, con.execute(target, "#Test\n", output)) <<
189           "No-op command did not succeed at iteration " << i;
190   }
191 }
192 
TEST_F(IptablesRestoreControllerTest,TestRestartOnProcessDeath)193 TEST_F(IptablesRestoreControllerTest, TestRestartOnProcessDeath) {
194   std::string output;
195 
196   // Run a command to ensure that the processes are running.
197   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", &output));
198 
199   pid_t pid4 = getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS);
200   pid_t pid6 = getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS);
201 
202   ASSERT_EQ(0, kill(pid4, 0)) << "iptables-restore pid " << pid4 << " does not exist";
203   ASSERT_EQ(0, kill(pid6, 0)) << "ip6tables-restore pid " << pid6 << " does not exist";
204   ASSERT_EQ(0, kill(pid4, SIGTERM)) << "Failed to send SIGTERM to iptables-restore pid " << pid4;
205   ASSERT_EQ(0, kill(pid6, SIGTERM)) << "Failed to send SIGTERM to ip6tables-restore pid " << pid6;
206 
207   // Wait 100ms for processes to terminate.
208   TEMP_FAILURE_RETRY(usleep(100 * 1000));
209 
210   // Ensure that running a new command properly restarts the processes.
211   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, "#Test\n", nullptr));
212   EXPECT_NE(pid4, getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS));
213   EXPECT_NE(pid6, getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS));
214 
215   // Check there are no zombies.
216   expectNoIptablesRestoreProcess(pid4);
217   expectNoIptablesRestoreProcess(pid6);
218 }
219 
TEST_F(IptablesRestoreControllerTest,TestCommandTimeout)220 TEST_F(IptablesRestoreControllerTest, TestCommandTimeout) {
221   // Don't wait 10 seconds for this test to fail.
222   setRetryParameters(3, 50);
223 
224   // Expected contents of the chain.
225   std::vector<std::string> expectedLines = {
226       StringPrintf("Chain %s (0 references)", mChainName.c_str()),
227       "target     prot opt source               destination         ",
228       "RETURN     all  --  0.0.0.0/0            0.0.0.0/0           ",
229       StringPrintf("Chain %s (0 references)", mChainName.c_str()),
230       "target     prot opt source               destination         ",
231       "RETURN     all      ::/0                 ::/0                ",
232       ""
233   };
234   std::string expected = Join(expectedLines, "\n");
235 
236   std::vector<std::string> listCommands = {
237       "*filter",
238       StringPrintf("-n -L %s", mChainName.c_str()),  // List chain.
239       "COMMIT",
240       ""
241   };
242   std::string commandString = Join(listCommands, "\n");
243   std::string output;
244 
245   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, commandString, &output));
246   EXPECT_EQ(expected, output);
247 
248   ASSERT_EQ(0, acquireIptablesLock());
249   EXPECT_EQ(-1, con.execute(IptablesTarget::V4V6, commandString, &output));
250   EXPECT_EQ(-1, con.execute(IptablesTarget::V4V6, commandString, &output));
251   releaseIptablesLock();
252 
253   EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, commandString, &output));
254   EXPECT_EQ(expected, output);
255 }
256 
257 
TEST_F(IptablesRestoreControllerTest,TestUidRuleBenchmark)258 TEST_F(IptablesRestoreControllerTest, TestUidRuleBenchmark) {
259     const std::vector<int> ITERATIONS = { 1, 5, 10 };
260 
261     const std::string IPTABLES_RESTORE_ADD =
262             StringPrintf("*filter\n-I %s -m owner --uid-owner 2000000000 -j RETURN\nCOMMIT\n",
263                          mChainName.c_str());
264     const std::string IPTABLES_RESTORE_DEL =
265             StringPrintf("*filter\n-D %s -m owner --uid-owner 2000000000 -j RETURN\nCOMMIT\n",
266                          mChainName.c_str());
267 
268     for (const int iterations : ITERATIONS) {
269         Stopwatch s;
270         for (int i = 0; i < iterations; i++) {
271             EXPECT_EQ(0, con.execute(V4V6, IPTABLES_RESTORE_ADD, nullptr));
272             EXPECT_EQ(0, con.execute(V4V6, IPTABLES_RESTORE_DEL, nullptr));
273         }
274         float timeTaken = s.getTimeAndReset();
275         fprintf(stderr, "    Add/del %d UID rules via restore: %.1fms (%.2fms per operation)\n",
276                 iterations, timeTaken, timeTaken / 2 / iterations);
277     }
278 }
279 
TEST_F(IptablesRestoreControllerTest,TestStartup)280 TEST_F(IptablesRestoreControllerTest, TestStartup) {
281   // Tests that IptablesRestoreController::Init never sets its processes to null pointers if
282   // fork() succeeds.
283   {
284     // Mock fork(), and check that initializing 100 times never results in a null pointer.
285     constexpr int NUM_ITERATIONS = 100;  // Takes 100-150ms on angler.
286     constexpr pid_t FAKE_PID = 2000000001;
287     StrictMock<ScopedMockSyscalls> sys;
288 
289     EXPECT_CALL(sys, fork()).Times(NUM_ITERATIONS * 2).WillRepeatedly(Return(FAKE_PID));
290     for (int i = 0; i < NUM_ITERATIONS; i++) {
291       Init();
292       EXPECT_NE(0, getIpRestorePid(IptablesRestoreController::IPTABLES_PROCESS));
293       EXPECT_NE(0, getIpRestorePid(IptablesRestoreController::IP6TABLES_PROCESS));
294     }
295   }
296 
297   // The controller is now in an invalid state: the pipes are connected to working iptables
298   // processes, but the PIDs are set to FAKE_PID. Send a malformed command to ensure that the
299   // processes terminate and close the pipes, then send a valid command to have the controller
300   // re-initialize properly now that fork() is no longer mocked.
301   EXPECT_EQ(-1, con.execute(V4V6, "malformed command\n", nullptr));
302   EXPECT_EQ(0, con.execute(V4V6, "#Test\n", nullptr));
303 }
304