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