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