Home | History | Annotate | Download | only in server
      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 
     49   static void SetUpTestCase() {
     50       blockSigpipe();
     51   }
     52 
     53   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   }
     62 
     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   }
     90 
     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() {
    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 
    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   }
    146 };
    147 
    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 
    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 
    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 
    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 
    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