github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/iptables.cc (about)

     1  // Copyright 2019 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include "test/syscalls/linux/iptables.h"
    16  
    17  #include <arpa/inet.h>
    18  #include <linux/capability.h>
    19  #include <linux/netfilter/x_tables.h>
    20  #include <net/if.h>
    21  #include <netinet/in.h>
    22  #include <netinet/ip.h>
    23  #include <netinet/ip_icmp.h>
    24  #include <stdio.h>
    25  #include <sys/poll.h>
    26  #include <sys/socket.h>
    27  #include <sys/types.h>
    28  #include <unistd.h>
    29  
    30  #include <algorithm>
    31  
    32  #include "gtest/gtest.h"
    33  #include "test/util/capability_util.h"
    34  #include "test/util/file_descriptor.h"
    35  #include "test/util/test_util.h"
    36  
    37  namespace gvisor {
    38  namespace testing {
    39  
    40  namespace {
    41  
    42  constexpr char kNatTablename[] = "nat";
    43  constexpr char kErrorTarget[] = "ERROR";
    44  constexpr size_t kEmptyStandardEntrySize =
    45      sizeof(struct ipt_entry) + sizeof(struct ipt_standard_target);
    46  constexpr size_t kEmptyErrorEntrySize =
    47      sizeof(struct ipt_entry) + sizeof(struct ipt_error_target);
    48  
    49  TEST(IPTablesBasic, CreateSocket) {
    50    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    51  
    52    int sock;
    53    ASSERT_THAT(sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP),
    54                SyscallSucceeds());
    55  
    56    ASSERT_THAT(close(sock), SyscallSucceeds());
    57  }
    58  
    59  TEST(IPTablesBasic, FailSockoptNonRaw) {
    60    // Even if the user has CAP_NET_RAW, they shouldn't be able to use the
    61    // iptables sockopts with a non-raw socket.
    62    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    63  
    64    int sock;
    65    ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds());
    66  
    67    struct ipt_getinfo info = {};
    68    snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
    69    socklen_t info_size = sizeof(info);
    70    EXPECT_THAT(getsockopt(sock, SOL_IP, IPT_SO_GET_INFO, &info, &info_size),
    71                SyscallFailsWithErrno(ENOPROTOOPT));
    72  
    73    ASSERT_THAT(close(sock), SyscallSucceeds());
    74  }
    75  
    76  TEST(IPTablesBasic, GetInfoErrorPrecedence) {
    77    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    78  
    79    int sock;
    80    ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds());
    81  
    82    // When using the wrong type of socket and a too-short optlen, we should get
    83    // EINVAL.
    84    struct ipt_getinfo info = {};
    85    snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
    86    socklen_t info_size = sizeof(info) - 1;
    87    ASSERT_THAT(getsockopt(sock, SOL_IP, IPT_SO_GET_INFO, &info, &info_size),
    88                SyscallFailsWithErrno(EINVAL));
    89  }
    90  
    91  TEST(IPTablesBasic, GetEntriesErrorPrecedence) {
    92    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    93  
    94    int sock;
    95    ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds());
    96  
    97    // When using the wrong type of socket and a too-short optlen, we should get
    98    // EINVAL.
    99    struct ipt_get_entries entries = {};
   100    socklen_t entries_size = sizeof(struct ipt_get_entries) - 1;
   101    snprintf(entries.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
   102    ASSERT_THAT(
   103        getsockopt(sock, SOL_IP, IPT_SO_GET_ENTRIES, &entries, &entries_size),
   104        SyscallFailsWithErrno(EINVAL));
   105  }
   106  
   107  TEST(IPTablesBasic, OriginalDstErrors) {
   108    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
   109  
   110    int sock;
   111    ASSERT_THAT(sock = socket(AF_INET, SOCK_STREAM, 0), SyscallSucceeds());
   112  
   113    // Sockets not affected by NAT should fail to find an original destination.
   114    struct sockaddr_in addr = {};
   115    socklen_t addr_len = sizeof(addr);
   116    EXPECT_THAT(getsockopt(sock, SOL_IP, SO_ORIGINAL_DST, &addr, &addr_len),
   117                SyscallFailsWithErrno(ENOTCONN));
   118  }
   119  
   120  TEST(IPTablesBasic, GetRevision) {
   121    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
   122  
   123    int sock;
   124    ASSERT_THAT(sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP),
   125                SyscallSucceeds());
   126  
   127    struct xt_get_revision rev = {};
   128    socklen_t rev_len = sizeof(rev);
   129  
   130    snprintf(rev.name, sizeof(rev.name), "REDIRECT");
   131    rev.revision = 0;
   132  
   133    // Revision 0 exists.
   134    EXPECT_THAT(
   135        getsockopt(sock, SOL_IP, IPT_SO_GET_REVISION_TARGET, &rev, &rev_len),
   136        SyscallSucceeds());
   137    EXPECT_EQ(rev.revision, 0);
   138  
   139    // Revisions > 0 don't exist.
   140    rev.revision = 1;
   141    EXPECT_THAT(
   142        getsockopt(sock, SOL_IP, IPT_SO_GET_REVISION_TARGET, &rev, &rev_len),
   143        SyscallFailsWithErrno(EPROTONOSUPPORT));
   144  }
   145  
   146  // Fixture for iptables tests.
   147  class IPTablesTest : public ::testing::Test {
   148   protected:
   149    // Creates a socket to be used in tests.
   150    void SetUp() override;
   151  
   152    // Closes the socket created by SetUp().
   153    void TearDown() override;
   154  
   155    // The socket via which to manipulate iptables.
   156    int s_;
   157  };
   158  
   159  void IPTablesTest::SetUp() {
   160    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
   161  
   162    ASSERT_THAT(s_ = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), SyscallSucceeds());
   163  }
   164  
   165  void IPTablesTest::TearDown() {
   166    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
   167  
   168    EXPECT_THAT(close(s_), SyscallSucceeds());
   169  }
   170  
   171  // This tests the initial state of a machine with empty iptables. We don't have
   172  // a guarantee that the iptables are empty when running in native, but we can
   173  // test that gVisor has the same initial state that a newly-booted Linux machine
   174  // would have.
   175  TEST_F(IPTablesTest, InitialState) {
   176    SKIP_IF(!IsRunningOnGvisor());
   177    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
   178  
   179    //
   180    // Get info via sockopt.
   181    //
   182    struct ipt_getinfo info = {};
   183    snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
   184    socklen_t info_size = sizeof(info);
   185    ASSERT_THAT(getsockopt(s_, SOL_IP, IPT_SO_GET_INFO, &info, &info_size),
   186                SyscallSucceeds());
   187  
   188    // The nat table supports PREROUTING, and OUTPUT.
   189    unsigned int valid_hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT) |
   190                               (1 << NF_IP_POST_ROUTING) | (1 << NF_IP_LOCAL_IN);
   191  
   192    EXPECT_EQ(info.valid_hooks, valid_hooks);
   193  
   194    // Each chain consists of an empty entry with a standard target..
   195    EXPECT_EQ(info.hook_entry[NF_IP_PRE_ROUTING], 0);
   196    EXPECT_EQ(info.hook_entry[NF_IP_LOCAL_IN], kEmptyStandardEntrySize);
   197    EXPECT_EQ(info.hook_entry[NF_IP_LOCAL_OUT], kEmptyStandardEntrySize * 2);
   198    EXPECT_EQ(info.hook_entry[NF_IP_POST_ROUTING], kEmptyStandardEntrySize * 3);
   199  
   200    // The underflow points are the same as the entry points.
   201    EXPECT_EQ(info.underflow[NF_IP_PRE_ROUTING], 0);
   202    EXPECT_EQ(info.underflow[NF_IP_LOCAL_IN], kEmptyStandardEntrySize);
   203    EXPECT_EQ(info.underflow[NF_IP_LOCAL_OUT], kEmptyStandardEntrySize * 2);
   204    EXPECT_EQ(info.underflow[NF_IP_POST_ROUTING], kEmptyStandardEntrySize * 3);
   205  
   206    // One entry for each chain, plus an error entry at the end.
   207    EXPECT_EQ(info.num_entries, 5);
   208  
   209    EXPECT_EQ(info.size, 4 * kEmptyStandardEntrySize + kEmptyErrorEntrySize);
   210    EXPECT_EQ(strcmp(info.name, kNatTablename), 0);
   211  
   212    //
   213    // Use info to get entries.
   214    //
   215    socklen_t entries_size = sizeof(struct ipt_get_entries) + info.size;
   216    struct ipt_get_entries* entries =
   217        static_cast<struct ipt_get_entries*>(malloc(entries_size));
   218    snprintf(entries->name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
   219    entries->size = info.size;
   220    ASSERT_THAT(
   221        getsockopt(s_, SOL_IP, IPT_SO_GET_ENTRIES, entries, &entries_size),
   222        SyscallSucceeds());
   223  
   224    // Verify the name and size.
   225    ASSERT_EQ(info.size, entries->size);
   226    ASSERT_EQ(strcmp(entries->name, kNatTablename), 0);
   227  
   228    // Verify that the entrytable is 4 entries with accept targets and no matches
   229    // followed by a single error target.
   230    size_t entry_offset = 0;
   231    while (entry_offset < entries->size) {
   232      struct ipt_entry* entry = reinterpret_cast<struct ipt_entry*>(
   233          reinterpret_cast<char*>(entries->entrytable) + entry_offset);
   234  
   235      // ip should be zeroes.
   236      struct ipt_ip zeroed = {};
   237      EXPECT_EQ(memcmp(static_cast<void*>(&zeroed),
   238                       static_cast<void*>(&entry->ip), sizeof(zeroed)),
   239                0);
   240  
   241      // target_offset should be zero.
   242      EXPECT_EQ(entry->target_offset, sizeof(ipt_entry));
   243  
   244      if (entry_offset < kEmptyStandardEntrySize * 4) {
   245        // The first 4 entries are standard targets
   246        struct ipt_standard_target* target =
   247            reinterpret_cast<struct ipt_standard_target*>(entry->elems);
   248        EXPECT_EQ(entry->next_offset, kEmptyStandardEntrySize);
   249        EXPECT_EQ(target->target.u.user.target_size, sizeof(*target));
   250        EXPECT_EQ(strcmp(target->target.u.user.name, ""), 0);
   251        EXPECT_EQ(target->target.u.user.revision, 0);
   252        // This is what's returned for an accept verdict. I don't know why.
   253        EXPECT_EQ(target->verdict, -NF_ACCEPT - 1);
   254      } else {
   255        // The last entry is an error target
   256        struct ipt_error_target* target =
   257            reinterpret_cast<struct ipt_error_target*>(entry->elems);
   258        EXPECT_EQ(entry->next_offset, kEmptyErrorEntrySize);
   259        EXPECT_EQ(target->target.u.user.target_size, sizeof(*target));
   260        EXPECT_EQ(strcmp(target->target.u.user.name, kErrorTarget), 0);
   261        EXPECT_EQ(target->target.u.user.revision, 0);
   262        EXPECT_EQ(strcmp(target->errorname, kErrorTarget), 0);
   263      }
   264  
   265      entry_offset += entry->next_offset;
   266    }
   267  
   268    free(entries);
   269  }
   270  
   271  }  // namespace
   272  
   273  }  // namespace testing
   274  }  // namespace gvisor