github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/tuntap.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 <arpa/inet.h> 16 #include <linux/capability.h> 17 #include <linux/if_arp.h> 18 #include <linux/if_ether.h> 19 #include <linux/if_tun.h> 20 #include <netinet/ip.h> 21 #include <netinet/ip_icmp.h> 22 #include <poll.h> 23 #include <sys/ioctl.h> 24 #include <sys/socket.h> 25 #include <sys/types.h> 26 27 #include <cstddef> 28 29 #include "gmock/gmock.h" 30 #include "gtest/gtest.h" 31 #include "absl/strings/ascii.h" 32 #include "absl/strings/str_split.h" 33 #include "test/syscalls/linux/socket_netlink_route_util.h" 34 #include "test/syscalls/linux/socket_test_util.h" 35 #include "test/util/capability_util.h" 36 #include "test/util/file_descriptor.h" 37 #include "test/util/fs_util.h" 38 #include "test/util/posix_error.h" 39 #include "test/util/test_util.h" 40 41 namespace gvisor { 42 namespace testing { 43 namespace { 44 45 constexpr int kIPLen = 4; 46 47 constexpr const char kDevNetTun[] = "/dev/net/tun"; 48 constexpr const char kTapName[] = "tap0"; 49 constexpr const char kTunName[] = "tun0"; 50 51 #define kTapIPAddr htonl(0x0a000001) /* Inet 10.0.0.1 */ 52 #define kTapPeerIPAddr htonl(0x0a000002) /* Inet 10.0.0.2 */ 53 54 constexpr const uint8_t kMacA[ETH_ALEN] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; 55 constexpr const uint8_t kMacB[ETH_ALEN] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB}; 56 57 PosixErrorOr<std::set<std::string>> DumpLinkNames() { 58 ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks()); 59 std::set<std::string> names; 60 for (const auto& link : links) { 61 names.emplace(link.name); 62 } 63 return names; 64 } 65 66 PosixErrorOr<Link> GetLinkByName(const std::string& name) { 67 ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks()); 68 for (const auto& link : links) { 69 if (link.name == name) { 70 return link; 71 } 72 } 73 return PosixError(ENOENT, "interface not found"); 74 } 75 76 struct pihdr { 77 uint16_t pi_flags; 78 uint16_t pi_protocol; 79 } __attribute__((packed)); 80 81 struct ping_pkt { 82 pihdr pi; 83 struct ethhdr eth; 84 struct iphdr ip; 85 struct icmphdr icmp; 86 char payload[64]; 87 } __attribute__((packed)); 88 89 ping_pkt CreatePingPacket(const uint8_t srcmac[ETH_ALEN], const in_addr_t srcip, 90 const uint8_t dstmac[ETH_ALEN], 91 const in_addr_t dstip) { 92 ping_pkt pkt = {}; 93 94 pkt.pi.pi_protocol = htons(ETH_P_IP); 95 96 memcpy(pkt.eth.h_dest, dstmac, sizeof(pkt.eth.h_dest)); 97 memcpy(pkt.eth.h_source, srcmac, sizeof(pkt.eth.h_source)); 98 pkt.eth.h_proto = htons(ETH_P_IP); 99 100 pkt.ip.ihl = 5; 101 pkt.ip.version = 4; 102 pkt.ip.tos = 0; 103 pkt.ip.tot_len = htons(sizeof(struct iphdr) + sizeof(struct icmphdr) + 104 sizeof(pkt.payload)); 105 pkt.ip.id = 1; 106 pkt.ip.frag_off = 1 << 6; // Do not fragment 107 pkt.ip.ttl = 64; 108 pkt.ip.protocol = IPPROTO_ICMP; 109 pkt.ip.daddr = dstip; 110 pkt.ip.saddr = srcip; 111 pkt.ip.check = IPChecksum(pkt.ip); 112 113 pkt.icmp.type = ICMP_ECHO; 114 pkt.icmp.code = 0; 115 pkt.icmp.checksum = 0; 116 pkt.icmp.un.echo.sequence = 1; 117 pkt.icmp.un.echo.id = 1; 118 119 strncpy(pkt.payload, "abcd", sizeof(pkt.payload)); 120 pkt.icmp.checksum = ICMPChecksum(pkt.icmp, pkt.payload, sizeof(pkt.payload)); 121 122 return pkt; 123 } 124 125 struct arp_pkt { 126 pihdr pi; 127 struct ethhdr eth; 128 struct arphdr arp; 129 uint8_t arp_sha[ETH_ALEN]; 130 uint8_t arp_spa[kIPLen]; 131 uint8_t arp_tha[ETH_ALEN]; 132 uint8_t arp_tpa[kIPLen]; 133 } __attribute__((packed)); 134 135 std::string CreateArpPacket(const uint8_t srcmac[ETH_ALEN], 136 const in_addr_t srcip, 137 const uint8_t dstmac[ETH_ALEN], 138 const in_addr_t dstip) { 139 std::string buffer; 140 buffer.resize(sizeof(arp_pkt)); 141 142 arp_pkt* pkt = reinterpret_cast<arp_pkt*>(&buffer[0]); 143 { 144 pkt->pi.pi_protocol = htons(ETH_P_ARP); 145 146 memcpy(pkt->eth.h_dest, kMacA, sizeof(pkt->eth.h_dest)); 147 memcpy(pkt->eth.h_source, kMacB, sizeof(pkt->eth.h_source)); 148 pkt->eth.h_proto = htons(ETH_P_ARP); 149 150 pkt->arp.ar_hrd = htons(ARPHRD_ETHER); 151 pkt->arp.ar_pro = htons(ETH_P_IP); 152 pkt->arp.ar_hln = ETH_ALEN; 153 pkt->arp.ar_pln = kIPLen; 154 pkt->arp.ar_op = htons(ARPOP_REPLY); 155 156 memcpy(pkt->arp_sha, srcmac, sizeof(pkt->arp_sha)); 157 memcpy(pkt->arp_spa, &srcip, sizeof(pkt->arp_spa)); 158 memcpy(pkt->arp_tha, dstmac, sizeof(pkt->arp_tha)); 159 memcpy(pkt->arp_tpa, &dstip, sizeof(pkt->arp_tpa)); 160 } 161 return buffer; 162 } 163 164 } // namespace 165 166 TEST(TuntapStaticTest, NetTunExists) { 167 struct stat statbuf; 168 ASSERT_THAT(stat(kDevNetTun, &statbuf), SyscallSucceeds()); 169 // Check that it's a character device with rw-rw-rw- permissions. 170 EXPECT_EQ(statbuf.st_mode, S_IFCHR | 0666); 171 } 172 173 class TuntapTest : public ::testing::Test { 174 protected: 175 void SetUp() override { 176 const bool have_net_admin_cap = 177 ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)); 178 179 if (have_net_admin_cap && !IsRunningOnGvisor()) { 180 // gVisor always creates enabled/up'd interfaces, while Linux does not (as 181 // observed in b/110961832). Some of the tests require the Linux stack to 182 // notify the socket of any link-address-resolution failures. Those 183 // notifications do not seem to show up when the loopback interface in the 184 // namespace is down. 185 auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName("lo")); 186 ASSERT_NO_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP)); 187 } 188 } 189 }; 190 191 TEST_F(TuntapTest, CreateInterfaceNoCap) { 192 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 193 194 AutoCapability cap(CAP_NET_ADMIN, false); 195 196 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); 197 198 struct ifreq ifr = {}; 199 ifr.ifr_flags = IFF_TAP; 200 strncpy(ifr.ifr_name, kTapName, IFNAMSIZ); 201 202 EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallFailsWithErrno(EPERM)); 203 } 204 205 TEST_F(TuntapTest, CreateFixedNameInterface) { 206 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 207 208 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); 209 210 struct ifreq ifr_set = {}; 211 ifr_set.ifr_flags = IFF_TAP; 212 strncpy(ifr_set.ifr_name, kTapName, IFNAMSIZ); 213 EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set), 214 SyscallSucceedsWithValue(0)); 215 216 struct ifreq ifr_get = {}; 217 EXPECT_THAT(ioctl(fd.get(), TUNGETIFF, &ifr_get), 218 SyscallSucceedsWithValue(0)); 219 220 struct ifreq ifr_expect = ifr_set; 221 // See __tun_chr_ioctl() in net/drivers/tun.c. 222 ifr_expect.ifr_flags |= IFF_NOFILTER; 223 224 EXPECT_THAT(DumpLinkNames(), 225 IsPosixErrorOkAndHolds(::testing::Contains(kTapName))); 226 EXPECT_THAT(memcmp(&ifr_expect, &ifr_get, sizeof(ifr_get)), ::testing::Eq(0)); 227 } 228 229 TEST_F(TuntapTest, CreateInterface) { 230 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 231 232 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); 233 234 struct ifreq ifr = {}; 235 ifr.ifr_flags = IFF_TAP; 236 // Empty ifr.ifr_name. Let kernel assign. 237 238 EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallSucceedsWithValue(0)); 239 240 struct ifreq ifr_get = {}; 241 EXPECT_THAT(ioctl(fd.get(), TUNGETIFF, &ifr_get), 242 SyscallSucceedsWithValue(0)); 243 244 std::string ifname = ifr_get.ifr_name; 245 EXPECT_THAT(ifname, ::testing::StartsWith("tap")); 246 EXPECT_THAT(DumpLinkNames(), 247 IsPosixErrorOkAndHolds(::testing::Contains(ifname))); 248 } 249 250 TEST_F(TuntapTest, InvalidReadWrite) { 251 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 252 253 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); 254 255 char buf[128] = {}; 256 EXPECT_THAT(read(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EBADFD)); 257 EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EBADFD)); 258 } 259 260 TEST_F(TuntapTest, WriteToDownDevice) { 261 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 262 263 // FIXME(b/110961832): gVisor always creates enabled/up'd interfaces. 264 SKIP_IF(IsRunningOnGvisor()); 265 266 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); 267 268 // Device created should be down by default. 269 struct ifreq ifr = {}; 270 ifr.ifr_flags = IFF_TAP; 271 EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallSucceedsWithValue(0)); 272 273 char buf[128] = {}; 274 EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EIO)); 275 } 276 277 PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name, 278 const in_addr_t dev_addr) { 279 // Interface creation. 280 ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, Open(kDevNetTun, O_RDWR)); 281 282 struct ifreq ifr_set = {}; 283 ifr_set.ifr_flags = IFF_TAP; 284 strncpy(ifr_set.ifr_name, dev_name.c_str(), IFNAMSIZ); 285 if (ioctl(fd.get(), TUNSETIFF, &ifr_set) < 0) { 286 return PosixError(errno); 287 } 288 289 ASSIGN_OR_RETURN_ERRNO(auto link, GetLinkByName(dev_name)); 290 291 const struct in_addr dev_ipv4_addr = {.s_addr = dev_addr}; 292 // Interface setup. 293 EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, /*prefixlen=*/24, 294 &dev_ipv4_addr, sizeof(dev_ipv4_addr))); 295 296 if (!IsRunningOnGvisor()) { 297 // FIXME(b/110961832): gVisor doesn't support setting MAC address on 298 // interfaces yet. 299 RETURN_IF_ERRNO(LinkSetMacAddr(link.index, kMacA, sizeof(kMacA))); 300 301 // FIXME(b/110961832): gVisor always creates enabled/up'd interfaces. 302 RETURN_IF_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP)); 303 } 304 305 return fd; 306 } 307 308 // This test sets up a TAP device and pings kernel by sending ICMP echo request. 309 // 310 // It works as the following: 311 // * Open /dev/net/tun, and create kTapName interface. 312 // * Use rtnetlink to do initial setup of the interface: 313 // * Assign IP address 10.0.0.1/24 to kernel. 314 // * MAC address: kMacA 315 // * Bring up the interface. 316 // * Send an ICMP echo reqest (ping) packet from 10.0.0.2 (kMacB) to kernel. 317 // * Loop to receive packets from TAP device/fd: 318 // * If packet is an ICMP echo reply, it stops and passes the test. 319 // * If packet is an ARP request, it responds with canned reply and resends 320 // the 321 // ICMP request packet. 322 TEST_F(TuntapTest, PingKernel) { 323 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 324 325 FileDescriptor fd = 326 ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); 327 ping_pkt ping_req = 328 CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); 329 std::string arp_rep = 330 CreateArpPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); 331 332 // Send ping, this would trigger an ARP request on Linux. 333 EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)), 334 SyscallSucceedsWithValue(sizeof(ping_req))); 335 336 // Receive loop to process inbound packets. 337 struct inpkt { 338 union { 339 pihdr pi; 340 ping_pkt ping; 341 arp_pkt arp; 342 }; 343 }; 344 while (1) { 345 inpkt r = {}; 346 size_t n; 347 EXPECT_THAT(n = read(fd.get(), &r, sizeof(r)), SyscallSucceeds()); 348 349 if (n < sizeof(pihdr)) { 350 std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol 351 << " len: " << n << std::endl; 352 continue; 353 } 354 355 // Process ARP packet. 356 if (n >= sizeof(arp_pkt) && r.pi.pi_protocol == htons(ETH_P_ARP)) { 357 // Respond with canned ARP reply. 358 EXPECT_THAT(write(fd.get(), arp_rep.data(), arp_rep.size()), 359 SyscallSucceedsWithValue(arp_rep.size())); 360 // First ping request might have been dropped due to mac address not in 361 // ARP cache. Send it again. 362 EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)), 363 SyscallSucceedsWithValue(sizeof(ping_req))); 364 } 365 366 // Process ping response packet. 367 if (n >= sizeof(ping_pkt) && r.pi.pi_protocol == ping_req.pi.pi_protocol && 368 r.ping.ip.protocol == ping_req.ip.protocol && 369 !memcmp(&r.ping.ip.saddr, &ping_req.ip.daddr, kIPLen) && 370 !memcmp(&r.ping.ip.daddr, &ping_req.ip.saddr, kIPLen) && 371 r.ping.icmp.type == 0 && r.ping.icmp.code == 0) { 372 // Ends and passes the test. 373 break; 374 } 375 } 376 } 377 378 TEST_F(TuntapTest, SendUdpTriggersArpResolution) { 379 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 380 381 FileDescriptor fd = 382 ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); 383 384 // Send a UDP packet to remote. 385 int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); 386 ASSERT_THAT(sock, SyscallSucceeds()); 387 388 struct sockaddr_in remote = { 389 .sin_family = AF_INET, 390 .sin_port = htons(42), 391 .sin_addr = {.s_addr = kTapPeerIPAddr}, 392 }; 393 ASSERT_THAT(sendto(sock, "hello", 5, 0, AsSockAddr(&remote), sizeof(remote)), 394 SyscallSucceeds()); 395 396 struct inpkt { 397 union { 398 pihdr pi; 399 arp_pkt arp; 400 }; 401 }; 402 while (1) { 403 inpkt r = {}; 404 size_t n; 405 EXPECT_THAT(n = read(fd.get(), &r, sizeof(r)), SyscallSucceeds()); 406 407 if (n < sizeof(pihdr)) { 408 std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol 409 << " len: " << n << std::endl; 410 continue; 411 } 412 413 if (n >= sizeof(arp_pkt) && r.pi.pi_protocol == htons(ETH_P_ARP)) { 414 break; 415 } 416 } 417 } 418 419 TEST_F(TuntapTest, TUNNoPacketInfo) { 420 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 421 422 // Interface creation. 423 FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); 424 425 struct ifreq ifr_set = {}; 426 ifr_set.ifr_flags = IFF_TUN | IFF_NO_PI; 427 strncpy(ifr_set.ifr_name, kTunName, IFNAMSIZ); 428 EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set), SyscallSucceeds()); 429 430 // Interface setup. 431 auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName(kTunName)); 432 const struct in_addr dev_ipv4_addr = {.s_addr = kTapIPAddr}; 433 EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, 24, &dev_ipv4_addr, 434 sizeof(dev_ipv4_addr))); 435 436 ping_pkt ping_req = 437 CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); 438 size_t packet_size = sizeof(ping_req) - offsetof(ping_pkt, ip); 439 440 // Send ICMP query 441 EXPECT_THAT(write(fd.get(), &ping_req.ip, packet_size), 442 SyscallSucceedsWithValue(packet_size)); 443 444 // Receive loop to process inbound packets. 445 while (1) { 446 ping_pkt ping_resp = {}; 447 EXPECT_THAT(read(fd.get(), &ping_resp.ip, packet_size), 448 SyscallSucceedsWithValue(packet_size)); 449 450 // Process ping response packet. 451 if (!memcmp(&ping_resp.ip.saddr, &ping_req.ip.daddr, kIPLen) && 452 !memcmp(&ping_resp.ip.daddr, &ping_req.ip.saddr, kIPLen) && 453 ping_resp.icmp.type == 0 && ping_resp.icmp.code == 0) { 454 // Ends and passes the test. 455 break; 456 } 457 } 458 } 459 460 // TCPBlockingConnectFailsArpResolution tests for TCP connect to fail on link 461 // address resolution failure to a routable, but non existent peer. 462 TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) { 463 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 464 465 FileDescriptor sender = 466 ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); 467 468 FileDescriptor fd = 469 ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); 470 471 sockaddr_in connect_addr = { 472 .sin_family = AF_INET, 473 .sin_addr = {.s_addr = kTapPeerIPAddr}, 474 }; 475 ASSERT_THAT(connect(sender.get(), 476 reinterpret_cast<const struct sockaddr*>(&connect_addr), 477 sizeof(connect_addr)), 478 SyscallFailsWithErrno(EHOSTUNREACH)); 479 } 480 481 // TCPNonBlockingConnectFailsArpResolution tests for TCP non-blocking connect to 482 // to trigger an error event to be notified to poll on link address resolution 483 // failure to a routable, but non existent peer. 484 TEST_F(TuntapTest, TCPNonBlockingConnectFailsArpResolution) { 485 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 486 487 FileDescriptor sender = ASSERT_NO_ERRNO_AND_VALUE( 488 Socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); 489 490 FileDescriptor fd = 491 ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); 492 493 sockaddr_in connect_addr = { 494 .sin_family = AF_INET, 495 .sin_addr = {.s_addr = kTapPeerIPAddr}, 496 }; 497 ASSERT_THAT(connect(sender.get(), 498 reinterpret_cast<const struct sockaddr*>(&connect_addr), 499 sizeof(connect_addr)), 500 SyscallFailsWithErrno(EINPROGRESS)); 501 502 constexpr int kTimeout = 10000; 503 struct pollfd pfd = { 504 .fd = sender.get(), 505 .events = POLLIN | POLLOUT, 506 }; 507 ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); 508 ASSERT_EQ(pfd.revents, POLLIN | POLLOUT | POLLHUP | POLLERR); 509 510 ASSERT_THAT(connect(sender.get(), 511 reinterpret_cast<const struct sockaddr*>(&connect_addr), 512 sizeof(connect_addr)), 513 SyscallFailsWithErrno(EHOSTUNREACH)); 514 } 515 516 // Write hang bug found by syskaller: b/155928773 517 // https://syzkaller.appspot.com/bug?id=065b893bd8d1d04a4e0a1d53c578537cde1efe99 518 TEST_F(TuntapTest, WriteHangBug155928773) { 519 SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); 520 521 FileDescriptor fd = 522 ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); 523 524 int sock = socket(AF_INET, SOCK_DGRAM, 0); 525 ASSERT_THAT(sock, SyscallSucceeds()); 526 527 struct sockaddr_in remote = { 528 .sin_family = AF_INET, 529 .sin_port = htons(42), 530 .sin_addr = {.s_addr = kTapIPAddr}, 531 }; 532 // Return values do not matter in this test. 533 connect(sock, AsSockAddr(&remote), sizeof(remote)); 534 write(sock, "hello", 5); 535 } 536 537 } // namespace testing 538 } // namespace gvisor