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