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 <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 
     58   static void SetUpTestCase() {
     59       blockSigpipe();
     60   }
     61 
     62   void SetUp() {
     63     ASSERT_EQ(0, createTestChain());
     64   }
     65 
     66   void TearDown() {
     67     con.MAX_RETRIES = mDefaultMaxRetries;
     68     con.POLL_TIMEOUT_MS = mDefaultPollTimeoutMs;
     69     deleteTestChain();
     70   }
     71 
     72   void Init() {
     73     con.Init();
     74   }
     75 
     76   pid_t getIpRestorePid(const IptablesRestoreController::IptablesProcessType type) {
     77       return con.getIpRestorePid(type);
     78   };
     79 
     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 
    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 
    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 
    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 
    149   void releaseIptablesLock() {
    150     if (mIptablesLock != -1) {
    151       close(mIptablesLock);
    152     }
    153   }
    154 
    155   void setRetryParameters(int maxRetries, int pollTimeoutMs) {
    156     con.MAX_RETRIES = maxRetries;
    157     con.POLL_TIMEOUT_MS = pollTimeoutMs;
    158   }
    159 };
    160 
    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 
    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 
    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 
    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 
    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 
    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