summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/tuntap.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls/linux/tuntap.cc')
-rw-r--r--test/syscalls/linux/tuntap.cc503
1 files changed, 0 insertions, 503 deletions
diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc
deleted file mode 100644
index 6e3a00d2c..000000000
--- a/test/syscalls/linux/tuntap.cc
+++ /dev/null
@@ -1,503 +0,0 @@
-// Copyright 2019 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <arpa/inet.h>
-#include <linux/capability.h>
-#include <linux/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/if_tun.h>
-#include <netinet/ip.h>
-#include <netinet/ip_icmp.h>
-#include <poll.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "absl/strings/ascii.h"
-#include "absl/strings/str_split.h"
-#include "test/syscalls/linux/socket_netlink_route_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
-#include "test/util/capability_util.h"
-#include "test/util/file_descriptor.h"
-#include "test/util/fs_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/test_util.h"
-
-namespace gvisor {
-namespace testing {
-namespace {
-
-constexpr int kIPLen = 4;
-
-constexpr const char kDevNetTun[] = "/dev/net/tun";
-constexpr const char kTapName[] = "tap0";
-
-#define kTapIPAddr htonl(0x0a000001) /* Inet 10.0.0.1 */
-#define kTapPeerIPAddr htonl(0x0a000002) /* Inet 10.0.0.2 */
-
-constexpr const uint8_t kMacA[ETH_ALEN] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
-constexpr const uint8_t kMacB[ETH_ALEN] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB};
-
-PosixErrorOr<std::set<std::string>> DumpLinkNames() {
- ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks());
- std::set<std::string> names;
- for (const auto& link : links) {
- names.emplace(link.name);
- }
- return names;
-}
-
-PosixErrorOr<Link> GetLinkByName(const std::string& name) {
- ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks());
- for (const auto& link : links) {
- if (link.name == name) {
- return link;
- }
- }
- return PosixError(ENOENT, "interface not found");
-}
-
-struct pihdr {
- uint16_t pi_flags;
- uint16_t pi_protocol;
-} __attribute__((packed));
-
-struct ping_pkt {
- pihdr pi;
- struct ethhdr eth;
- struct iphdr ip;
- struct icmphdr icmp;
- char payload[64];
-} __attribute__((packed));
-
-ping_pkt CreatePingPacket(const uint8_t srcmac[ETH_ALEN], const in_addr_t srcip,
- const uint8_t dstmac[ETH_ALEN],
- const in_addr_t dstip) {
- ping_pkt pkt = {};
-
- pkt.pi.pi_protocol = htons(ETH_P_IP);
-
- memcpy(pkt.eth.h_dest, dstmac, sizeof(pkt.eth.h_dest));
- memcpy(pkt.eth.h_source, srcmac, sizeof(pkt.eth.h_source));
- pkt.eth.h_proto = htons(ETH_P_IP);
-
- pkt.ip.ihl = 5;
- pkt.ip.version = 4;
- pkt.ip.tos = 0;
- pkt.ip.tot_len = htons(sizeof(struct iphdr) + sizeof(struct icmphdr) +
- sizeof(pkt.payload));
- pkt.ip.id = 1;
- pkt.ip.frag_off = 1 << 6; // Do not fragment
- pkt.ip.ttl = 64;
- pkt.ip.protocol = IPPROTO_ICMP;
- pkt.ip.daddr = dstip;
- pkt.ip.saddr = srcip;
- pkt.ip.check = IPChecksum(pkt.ip);
-
- pkt.icmp.type = ICMP_ECHO;
- pkt.icmp.code = 0;
- pkt.icmp.checksum = 0;
- pkt.icmp.un.echo.sequence = 1;
- pkt.icmp.un.echo.id = 1;
-
- strncpy(pkt.payload, "abcd", sizeof(pkt.payload));
- pkt.icmp.checksum = ICMPChecksum(pkt.icmp, pkt.payload, sizeof(pkt.payload));
-
- return pkt;
-}
-
-struct arp_pkt {
- pihdr pi;
- struct ethhdr eth;
- struct arphdr arp;
- uint8_t arp_sha[ETH_ALEN];
- uint8_t arp_spa[kIPLen];
- uint8_t arp_tha[ETH_ALEN];
- uint8_t arp_tpa[kIPLen];
-} __attribute__((packed));
-
-std::string CreateArpPacket(const uint8_t srcmac[ETH_ALEN],
- const in_addr_t srcip,
- const uint8_t dstmac[ETH_ALEN],
- const in_addr_t dstip) {
- std::string buffer;
- buffer.resize(sizeof(arp_pkt));
-
- arp_pkt* pkt = reinterpret_cast<arp_pkt*>(&buffer[0]);
- {
- pkt->pi.pi_protocol = htons(ETH_P_ARP);
-
- memcpy(pkt->eth.h_dest, kMacA, sizeof(pkt->eth.h_dest));
- memcpy(pkt->eth.h_source, kMacB, sizeof(pkt->eth.h_source));
- pkt->eth.h_proto = htons(ETH_P_ARP);
-
- pkt->arp.ar_hrd = htons(ARPHRD_ETHER);
- pkt->arp.ar_pro = htons(ETH_P_IP);
- pkt->arp.ar_hln = ETH_ALEN;
- pkt->arp.ar_pln = kIPLen;
- pkt->arp.ar_op = htons(ARPOP_REPLY);
-
- memcpy(pkt->arp_sha, srcmac, sizeof(pkt->arp_sha));
- memcpy(pkt->arp_spa, &srcip, sizeof(pkt->arp_spa));
- memcpy(pkt->arp_tha, dstmac, sizeof(pkt->arp_tha));
- memcpy(pkt->arp_tpa, &dstip, sizeof(pkt->arp_tpa));
- }
- return buffer;
-}
-
-} // namespace
-
-TEST(TuntapStaticTest, NetTunExists) {
- struct stat statbuf;
- ASSERT_THAT(stat(kDevNetTun, &statbuf), SyscallSucceeds());
- // Check that it's a character device with rw-rw-rw- permissions.
- EXPECT_EQ(statbuf.st_mode, S_IFCHR | 0666);
-}
-
-class TuntapTest : public ::testing::Test {
- protected:
- void SetUp() override {
- have_net_admin_cap_ =
- ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN));
-
- if (have_net_admin_cap_ && !IsRunningOnGvisor()) {
- // gVisor always creates enabled/up'd interfaces, while Linux does not (as
- // observed in b/110961832). Some of the tests require the Linux stack to
- // notify the socket of any link-address-resolution failures. Those
- // notifications do not seem to show up when the loopback interface in the
- // namespace is down.
- auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName("lo"));
- ASSERT_NO_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP));
- }
- }
-
- void TearDown() override {
- if (have_net_admin_cap_) {
- // Bring back capability if we had dropped it in test case.
- ASSERT_NO_ERRNO(SetCapability(CAP_NET_ADMIN, true));
- }
- }
-
- bool have_net_admin_cap_;
-};
-
-TEST_F(TuntapTest, CreateInterfaceNoCap) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- ASSERT_NO_ERRNO(SetCapability(CAP_NET_ADMIN, false));
-
- FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
-
- struct ifreq ifr = {};
- ifr.ifr_flags = IFF_TAP;
- strncpy(ifr.ifr_name, kTapName, IFNAMSIZ);
-
- EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallFailsWithErrno(EPERM));
-}
-
-TEST_F(TuntapTest, CreateFixedNameInterface) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
-
- struct ifreq ifr_set = {};
- ifr_set.ifr_flags = IFF_TAP;
- strncpy(ifr_set.ifr_name, kTapName, IFNAMSIZ);
- EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set),
- SyscallSucceedsWithValue(0));
-
- struct ifreq ifr_get = {};
- EXPECT_THAT(ioctl(fd.get(), TUNGETIFF, &ifr_get),
- SyscallSucceedsWithValue(0));
-
- struct ifreq ifr_expect = ifr_set;
- // See __tun_chr_ioctl() in net/drivers/tun.c.
- ifr_expect.ifr_flags |= IFF_NOFILTER;
-
- EXPECT_THAT(DumpLinkNames(),
- IsPosixErrorOkAndHolds(::testing::Contains(kTapName)));
- EXPECT_THAT(memcmp(&ifr_expect, &ifr_get, sizeof(ifr_get)), ::testing::Eq(0));
-}
-
-TEST_F(TuntapTest, CreateInterface) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
-
- struct ifreq ifr = {};
- ifr.ifr_flags = IFF_TAP;
- // Empty ifr.ifr_name. Let kernel assign.
-
- EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallSucceedsWithValue(0));
-
- struct ifreq ifr_get = {};
- EXPECT_THAT(ioctl(fd.get(), TUNGETIFF, &ifr_get),
- SyscallSucceedsWithValue(0));
-
- std::string ifname = ifr_get.ifr_name;
- EXPECT_THAT(ifname, ::testing::StartsWith("tap"));
- EXPECT_THAT(DumpLinkNames(),
- IsPosixErrorOkAndHolds(::testing::Contains(ifname)));
-}
-
-TEST_F(TuntapTest, InvalidReadWrite) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
-
- char buf[128] = {};
- EXPECT_THAT(read(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EBADFD));
- EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EBADFD));
-}
-
-TEST_F(TuntapTest, WriteToDownDevice) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- // FIXME(b/110961832): gVisor always creates enabled/up'd interfaces.
- SKIP_IF(IsRunningOnGvisor());
-
- FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
-
- // Device created should be down by default.
- struct ifreq ifr = {};
- ifr.ifr_flags = IFF_TAP;
- EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallSucceedsWithValue(0));
-
- char buf[128] = {};
- EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EIO));
-}
-
-PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name,
- const in_addr_t dev_addr) {
- // Interface creation.
- ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, Open(kDevNetTun, O_RDWR));
-
- struct ifreq ifr_set = {};
- ifr_set.ifr_flags = IFF_TAP;
- strncpy(ifr_set.ifr_name, dev_name.c_str(), IFNAMSIZ);
- if (ioctl(fd.get(), TUNSETIFF, &ifr_set) < 0) {
- return PosixError(errno);
- }
-
- ASSIGN_OR_RETURN_ERRNO(auto link, GetLinkByName(dev_name));
-
- const struct in_addr dev_ipv4_addr = {.s_addr = dev_addr};
- // Interface setup.
- EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, /*prefixlen=*/24,
- &dev_ipv4_addr, sizeof(dev_ipv4_addr)));
-
- if (!IsRunningOnGvisor()) {
- // FIXME(b/110961832): gVisor doesn't support setting MAC address on
- // interfaces yet.
- RETURN_IF_ERRNO(LinkSetMacAddr(link.index, kMacA, sizeof(kMacA)));
-
- // FIXME(b/110961832): gVisor always creates enabled/up'd interfaces.
- RETURN_IF_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP));
- }
-
- return fd;
-}
-
-// This test sets up a TAP device and pings kernel by sending ICMP echo request.
-//
-// It works as the following:
-// * Open /dev/net/tun, and create kTapName interface.
-// * Use rtnetlink to do initial setup of the interface:
-// * Assign IP address 10.0.0.1/24 to kernel.
-// * MAC address: kMacA
-// * Bring up the interface.
-// * Send an ICMP echo reqest (ping) packet from 10.0.0.2 (kMacB) to kernel.
-// * Loop to receive packets from TAP device/fd:
-// * If packet is an ICMP echo reply, it stops and passes the test.
-// * If packet is an ARP request, it responds with canned reply and resends
-// the
-// ICMP request packet.
-TEST_F(TuntapTest, PingKernel) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
- ping_pkt ping_req =
- CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr);
- std::string arp_rep =
- CreateArpPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr);
-
- // Send ping, this would trigger an ARP request on Linux.
- EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)),
- SyscallSucceedsWithValue(sizeof(ping_req)));
-
- // Receive loop to process inbound packets.
- struct inpkt {
- union {
- pihdr pi;
- ping_pkt ping;
- arp_pkt arp;
- };
- };
- while (1) {
- inpkt r = {};
- size_t n;
- EXPECT_THAT(n = read(fd.get(), &r, sizeof(r)), SyscallSucceeds());
-
- if (n < sizeof(pihdr)) {
- std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol
- << " len: " << n << std::endl;
- continue;
- }
-
- // Process ARP packet.
- if (n >= sizeof(arp_pkt) && r.pi.pi_protocol == htons(ETH_P_ARP)) {
- // Respond with canned ARP reply.
- EXPECT_THAT(write(fd.get(), arp_rep.data(), arp_rep.size()),
- SyscallSucceedsWithValue(arp_rep.size()));
- // First ping request might have been dropped due to mac address not in
- // ARP cache. Send it again.
- EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)),
- SyscallSucceedsWithValue(sizeof(ping_req)));
- }
-
- // Process ping response packet.
- if (n >= sizeof(ping_pkt) && r.pi.pi_protocol == ping_req.pi.pi_protocol &&
- r.ping.ip.protocol == ping_req.ip.protocol &&
- !memcmp(&r.ping.ip.saddr, &ping_req.ip.daddr, kIPLen) &&
- !memcmp(&r.ping.ip.daddr, &ping_req.ip.saddr, kIPLen) &&
- r.ping.icmp.type == 0 && r.ping.icmp.code == 0) {
- // Ends and passes the test.
- break;
- }
- }
-}
-
-TEST_F(TuntapTest, SendUdpTriggersArpResolution) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
-
- // Send a UDP packet to remote.
- int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
- ASSERT_THAT(sock, SyscallSucceeds());
-
- struct sockaddr_in remote = {
- .sin_family = AF_INET,
- .sin_port = htons(42),
- .sin_addr = {.s_addr = kTapPeerIPAddr},
- };
- ASSERT_THAT(sendto(sock, "hello", 5, 0, AsSockAddr(&remote), sizeof(remote)),
- SyscallSucceeds());
-
- struct inpkt {
- union {
- pihdr pi;
- arp_pkt arp;
- };
- };
- while (1) {
- inpkt r = {};
- size_t n;
- EXPECT_THAT(n = read(fd.get(), &r, sizeof(r)), SyscallSucceeds());
-
- if (n < sizeof(pihdr)) {
- std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol
- << " len: " << n << std::endl;
- continue;
- }
-
- if (n >= sizeof(arp_pkt) && r.pi.pi_protocol == htons(ETH_P_ARP)) {
- break;
- }
- }
-}
-
-// TCPBlockingConnectFailsArpResolution tests for TCP connect to fail on link
-// address resolution failure to a routable, but non existent peer.
-TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor sender =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
-
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
-
- sockaddr_in connect_addr = {
- .sin_family = AF_INET,
- .sin_addr = {.s_addr = kTapPeerIPAddr},
- };
- ASSERT_THAT(connect(sender.get(),
- reinterpret_cast<const struct sockaddr*>(&connect_addr),
- sizeof(connect_addr)),
- SyscallFailsWithErrno(EHOSTUNREACH));
-}
-
-// TCPNonBlockingConnectFailsArpResolution tests for TCP non-blocking connect to
-// to trigger an error event to be notified to poll on link address resolution
-// failure to a routable, but non existent peer.
-TEST_F(TuntapTest, TCPNonBlockingConnectFailsArpResolution) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor sender = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
-
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
-
- sockaddr_in connect_addr = {
- .sin_family = AF_INET,
- .sin_addr = {.s_addr = kTapPeerIPAddr},
- };
- ASSERT_THAT(connect(sender.get(),
- reinterpret_cast<const struct sockaddr*>(&connect_addr),
- sizeof(connect_addr)),
- SyscallFailsWithErrno(EINPROGRESS));
-
- constexpr int kTimeout = 10000;
- struct pollfd pfd = {
- .fd = sender.get(),
- .events = POLLIN | POLLOUT,
- };
- ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
- ASSERT_EQ(pfd.revents, POLLIN | POLLOUT | POLLHUP | POLLERR);
-
- ASSERT_THAT(connect(sender.get(),
- reinterpret_cast<const struct sockaddr*>(&connect_addr),
- sizeof(connect_addr)),
- SyscallFailsWithErrno(EHOSTUNREACH));
-}
-
-// Write hang bug found by syskaller: b/155928773
-// https://syzkaller.appspot.com/bug?id=065b893bd8d1d04a4e0a1d53c578537cde1efe99
-TEST_F(TuntapTest, WriteHangBug155928773) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
-
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
-
- int sock = socket(AF_INET, SOCK_DGRAM, 0);
- ASSERT_THAT(sock, SyscallSucceeds());
-
- struct sockaddr_in remote = {
- .sin_family = AF_INET,
- .sin_port = htons(42),
- .sin_addr = {.s_addr = kTapIPAddr},
- };
- // Return values do not matter in this test.
- connect(sock, AsSockAddr(&remote), sizeof(remote));
- write(sock, "hello", 5);
-}
-
-} // namespace testing
-} // namespace gvisor