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

     1  // Copyright 2020 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 <linux/capability.h>
    16  #include <sys/socket.h>
    17  
    18  #include "gtest/gtest.h"
    19  #include "test/syscalls/linux/iptables.h"
    20  #include "test/syscalls/linux/socket_test_util.h"
    21  #include "test/util/capability_util.h"
    22  #include "test/util/file_descriptor.h"
    23  #include "test/util/test_util.h"
    24  
    25  namespace gvisor {
    26  namespace testing {
    27  
    28  namespace {
    29  
    30  constexpr char kNatTablename[] = "nat";
    31  constexpr char kErrorTarget[] = "ERROR";
    32  constexpr size_t kEmptyStandardEntrySize =
    33      sizeof(struct ip6t_entry) + sizeof(struct xt_standard_target);
    34  constexpr size_t kEmptyErrorEntrySize =
    35      sizeof(struct ip6t_entry) + sizeof(struct xt_error_target);
    36  
    37  TEST(IP6TablesBasic, FailSockoptNonRaw) {
    38    // Even if the user has CAP_NET_RAW, they shouldn't be able to use the
    39    // ip6tables sockopts with a non-raw socket.
    40    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    41  
    42    int sock;
    43    ASSERT_THAT(sock = socket(AF_INET6, SOCK_DGRAM, 0), SyscallSucceeds());
    44  
    45    struct ipt_getinfo info = {};
    46    snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
    47    socklen_t info_size = sizeof(info);
    48    EXPECT_THAT(getsockopt(sock, SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size),
    49                SyscallFailsWithErrno(ENOPROTOOPT));
    50  
    51    EXPECT_THAT(close(sock), SyscallSucceeds());
    52  }
    53  
    54  TEST(IP6TablesBasic, GetInfoErrorPrecedence) {
    55    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    56  
    57    int sock;
    58    ASSERT_THAT(sock = socket(AF_INET6, SOCK_DGRAM, 0), SyscallSucceeds());
    59  
    60    // When using the wrong type of socket and a too-short optlen, we should get
    61    // EINVAL.
    62    struct ipt_getinfo info = {};
    63    snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
    64    socklen_t info_size = sizeof(info) - 1;
    65    EXPECT_THAT(getsockopt(sock, SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size),
    66                SyscallFailsWithErrno(EINVAL));
    67  }
    68  
    69  TEST(IP6TablesBasic, GetEntriesErrorPrecedence) {
    70    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    71  
    72    int sock;
    73    ASSERT_THAT(sock = socket(AF_INET6, SOCK_DGRAM, 0), SyscallSucceeds());
    74  
    75    // When using the wrong type of socket and a too-short optlen, we should get
    76    // EINVAL.
    77    struct ip6t_get_entries entries = {};
    78    socklen_t entries_size = sizeof(struct ip6t_get_entries) - 1;
    79    snprintf(entries.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
    80    EXPECT_THAT(
    81        getsockopt(sock, SOL_IPV6, IP6T_SO_GET_ENTRIES, &entries, &entries_size),
    82        SyscallFailsWithErrno(EINVAL));
    83  }
    84  
    85  TEST(IP6TablesBasic, GetRevision) {
    86    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
    87  
    88    int sock;
    89    ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW),
    90                SyscallSucceeds());
    91  
    92    struct xt_get_revision rev = {};
    93    socklen_t rev_len = sizeof(rev);
    94  
    95    snprintf(rev.name, sizeof(rev.name), "REDIRECT");
    96    rev.revision = 0;
    97  
    98    // Revision 0 exists.
    99    EXPECT_THAT(
   100        getsockopt(sock, SOL_IPV6, IP6T_SO_GET_REVISION_TARGET, &rev, &rev_len),
   101        SyscallSucceeds());
   102    EXPECT_EQ(rev.revision, 0);
   103  
   104    // Revisions > 0 don't exist.
   105    rev.revision = 1;
   106    EXPECT_THAT(
   107        getsockopt(sock, SOL_IPV6, IP6T_SO_GET_REVISION_TARGET, &rev, &rev_len),
   108        SyscallFailsWithErrno(EPROTONOSUPPORT));
   109  }
   110  
   111  // This tests the initial state of a machine with empty ip6tables via
   112  // getsockopt(IP6T_SO_GET_INFO). We don't have a guarantee that the iptables are
   113  // empty when running in native, but we can test that gVisor has the same
   114  // initial state that a newly-booted Linux machine would have.
   115  TEST(IP6TablesTest, InitialInfo) {
   116    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
   117  
   118    FileDescriptor sock =
   119        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_RAW, IPPROTO_RAW));
   120  
   121    // Get info via sockopt.
   122    struct ipt_getinfo info = {};
   123    snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
   124    socklen_t info_size = sizeof(info);
   125    ASSERT_THAT(
   126        getsockopt(sock.get(), SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size),
   127        SyscallSucceeds());
   128  
   129    // The nat table supports PREROUTING, and OUTPUT.
   130    unsigned int valid_hooks =
   131        (1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_OUT) |
   132        (1 << NF_IP6_POST_ROUTING) | (1 << NF_IP6_LOCAL_IN);
   133    EXPECT_EQ(info.valid_hooks, valid_hooks);
   134  
   135    // Each chain consists of an empty entry with a standard target..
   136    EXPECT_EQ(info.hook_entry[NF_IP6_PRE_ROUTING], 0);
   137    EXPECT_EQ(info.hook_entry[NF_IP6_LOCAL_IN], kEmptyStandardEntrySize);
   138    EXPECT_EQ(info.hook_entry[NF_IP6_LOCAL_OUT], kEmptyStandardEntrySize * 2);
   139    EXPECT_EQ(info.hook_entry[NF_IP6_POST_ROUTING], kEmptyStandardEntrySize * 3);
   140  
   141    // The underflow points are the same as the entry points.
   142    EXPECT_EQ(info.underflow[NF_IP6_PRE_ROUTING], 0);
   143    EXPECT_EQ(info.underflow[NF_IP6_LOCAL_IN], kEmptyStandardEntrySize);
   144    EXPECT_EQ(info.underflow[NF_IP6_LOCAL_OUT], kEmptyStandardEntrySize * 2);
   145    EXPECT_EQ(info.underflow[NF_IP6_POST_ROUTING], kEmptyStandardEntrySize * 3);
   146  
   147    // One entry for each chain, plus an error entry at the end.
   148    EXPECT_EQ(info.num_entries, 5);
   149  
   150    EXPECT_EQ(info.size, 4 * kEmptyStandardEntrySize + kEmptyErrorEntrySize);
   151    EXPECT_EQ(strcmp(info.name, kNatTablename), 0);
   152  }
   153  
   154  // This tests the initial state of a machine with empty ip6tables via
   155  // getsockopt(IP6T_SO_GET_ENTRIES). We don't have a guarantee that the iptables
   156  // are empty when running in native, but we can test that gVisor has the same
   157  // initial state that a newly-booted Linux machine would have.
   158  TEST(IP6TablesTest, InitialEntries) {
   159    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
   160  
   161    FileDescriptor sock =
   162        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_RAW, IPPROTO_RAW));
   163  
   164    // Get info via sockopt.
   165    struct ipt_getinfo info = {};
   166    snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
   167    socklen_t info_size = sizeof(info);
   168    ASSERT_THAT(
   169        getsockopt(sock.get(), SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size),
   170        SyscallSucceeds());
   171  
   172    // Use info to get entries.
   173    socklen_t entries_size = sizeof(struct ip6t_get_entries) + info.size;
   174    struct ip6t_get_entries* entries =
   175        static_cast<struct ip6t_get_entries*>(malloc(entries_size));
   176    snprintf(entries->name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename);
   177    entries->size = info.size;
   178    ASSERT_THAT(getsockopt(sock.get(), SOL_IPV6, IP6T_SO_GET_ENTRIES, entries,
   179                           &entries_size),
   180                SyscallSucceeds());
   181  
   182    // Verify the name and size.
   183    ASSERT_EQ(info.size, entries->size);
   184    ASSERT_EQ(strcmp(entries->name, kNatTablename), 0);
   185  
   186    // Verify that the entrytable is 4 entries with accept targets and no matches
   187    // followed by a single error target.
   188    size_t entry_offset = 0;
   189    while (entry_offset < entries->size) {
   190      struct ip6t_entry* entry = reinterpret_cast<struct ip6t_entry*>(
   191          reinterpret_cast<char*>(entries->entrytable) + entry_offset);
   192  
   193      // ipv6 should be zeroed.
   194      struct ip6t_ip6 zeroed = {};
   195      ASSERT_EQ(memcmp(static_cast<void*>(&zeroed),
   196                       static_cast<void*>(&entry->ipv6), sizeof(zeroed)),
   197                0);
   198  
   199      // target_offset should be zero.
   200      EXPECT_EQ(entry->target_offset, sizeof(ip6t_entry));
   201  
   202      if (entry_offset < kEmptyStandardEntrySize * 4) {
   203        // The first 4 entries are standard targets
   204        struct xt_standard_target* target =
   205            reinterpret_cast<struct xt_standard_target*>(entry->elems);
   206        EXPECT_EQ(entry->next_offset, kEmptyStandardEntrySize);
   207        EXPECT_EQ(target->target.u.user.target_size, sizeof(*target));
   208        EXPECT_EQ(strcmp(target->target.u.user.name, ""), 0);
   209        EXPECT_EQ(target->target.u.user.revision, 0);
   210        // This is what's returned for an accept verdict. I don't know why.
   211        EXPECT_EQ(target->verdict, -NF_ACCEPT - 1);
   212      } else {
   213        // The last entry is an error target
   214        struct xt_error_target* target =
   215            reinterpret_cast<struct xt_error_target*>(entry->elems);
   216        EXPECT_EQ(entry->next_offset, kEmptyErrorEntrySize);
   217        EXPECT_EQ(target->target.u.user.target_size, sizeof(*target));
   218        EXPECT_EQ(strcmp(target->target.u.user.name, kErrorTarget), 0);
   219        EXPECT_EQ(target->target.u.user.revision, 0);
   220        EXPECT_EQ(strcmp(target->errorname, kErrorTarget), 0);
   221      }
   222  
   223      entry_offset += entry->next_offset;
   224      break;
   225    }
   226  
   227    free(entries);
   228  }
   229  
   230  }  // namespace
   231  
   232  }  // namespace testing
   233  }  // namespace gvisor