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