summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/packetimpact/runner/dut.go15
-rw-r--r--test/packetimpact/tests/BUILD12
-rw-r--r--test/packetimpact/tests/tcp_timewait_reset_test.go68
-rw-r--r--test/syscalls/linux/BUILD1
-rw-r--r--test/syscalls/linux/mount.cc22
-rw-r--r--test/syscalls/linux/prctl.cc2
-rw-r--r--test/syscalls/linux/proc.cc47
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc153
-rw-r--r--test/syscalls/linux/socket_netlink_route_util.cc87
-rw-r--r--test/syscalls/linux/socket_netlink_route_util.h4
-rw-r--r--test/syscalls/linux/stat.cc10
-rw-r--r--test/syscalls/linux/statfs.cc4
-rw-r--r--test/syscalls/linux/xattr.cc12
-rw-r--r--test/util/fs_util.cc14
-rw-r--r--test/util/fs_util.h7
15 files changed, 388 insertions, 70 deletions
diff --git a/test/packetimpact/runner/dut.go b/test/packetimpact/runner/dut.go
index be7b52f18..d4c486f9c 100644
--- a/test/packetimpact/runner/dut.go
+++ b/test/packetimpact/runner/dut.go
@@ -171,11 +171,8 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
}},
}
- // Add ctrlNet as eth1 and testNet as eth2.
- const testNetDev = "eth2"
-
device := mkDevice(dut)
- remoteIPv6, remoteMAC, dutDeviceID := device.Prepare(ctx, t, runOpts, ctrlNet, testNet, containerAddr)
+ remoteIPv6, remoteMAC, dutDeviceID, testNetDev := device.Prepare(ctx, t, runOpts, ctrlNet, testNet, containerAddr)
// Create the Docker container for the testbench.
testbench := dockerutil.MakeNativeContainer(ctx, logger("testbench"))
@@ -282,8 +279,8 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
// DUT describes how to setup/teardown the dut for packetimpact tests.
type DUT interface {
// Prepare prepares the dut, starts posix_server and returns the IPv6, MAC
- // address and the interface ID for the testNet on DUT.
- Prepare(ctx context.Context, t *testing.T, runOpts dockerutil.RunOpts, ctrlNet, testNet *dockerutil.Network, containerAddr net.IP) (net.IP, net.HardwareAddr, uint32)
+ // address, the interface ID, and the interface name for the testNet on DUT.
+ Prepare(ctx context.Context, t *testing.T, runOpts dockerutil.RunOpts, ctrlNet, testNet *dockerutil.Network, containerAddr net.IP) (net.IP, net.HardwareAddr, uint32, string)
// Logs retrieves the logs from the dut.
Logs(ctx context.Context) (string, error)
}
@@ -301,7 +298,7 @@ func NewDockerDUT(c *dockerutil.Container) DUT {
}
// Prepare implements DUT.Prepare.
-func (dut *DockerDUT) Prepare(ctx context.Context, t *testing.T, runOpts dockerutil.RunOpts, ctrlNet, testNet *dockerutil.Network, containerAddr net.IP) (net.IP, net.HardwareAddr, uint32) {
+func (dut *DockerDUT) Prepare(ctx context.Context, t *testing.T, runOpts dockerutil.RunOpts, ctrlNet, testNet *dockerutil.Network, containerAddr net.IP) (net.IP, net.HardwareAddr, uint32, string) {
const containerPosixServerBinary = "/packetimpact/posix_server"
dut.c.CopyFiles(&runOpts, "/packetimpact", "test/packetimpact/dut/posix_server")
@@ -345,7 +342,9 @@ func (dut *DockerDUT) Prepare(ctx context.Context, t *testing.T, runOpts dockeru
t.Fatalf("unable to set IPv6 address on container %s", dut.c.Name)
}
}
- return remoteIPv6, dutDeviceInfo.MAC, dutDeviceInfo.ID
+ const testNetDev = "eth2"
+
+ return remoteIPv6, dutDeviceInfo.MAC, dutDeviceInfo.ID, testNetDev
}
// Logs implements DUT.Logs.
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
index e1ed0cc60..6dda05102 100644
--- a/test/packetimpact/tests/BUILD
+++ b/test/packetimpact/tests/BUILD
@@ -260,6 +260,18 @@ packetimpact_go_test(
)
packetimpact_go_test(
+ name = "tcp_timewait_reset",
+ srcs = ["tcp_timewait_reset_test.go"],
+ # TODO(b/168523247): Fix netstack then remove the line below.
+ expect_netstack_failure = True,
+ deps = [
+ "//pkg/tcpip/header",
+ "//test/packetimpact/testbench",
+ "@org_golang_x_sys//unix:go_default_library",
+ ],
+)
+
+packetimpact_go_test(
name = "icmpv6_param_problem",
srcs = ["icmpv6_param_problem_test.go"],
# TODO(b/153485026): Fix netstack then remove the line below.
diff --git a/test/packetimpact/tests/tcp_timewait_reset_test.go b/test/packetimpact/tests/tcp_timewait_reset_test.go
new file mode 100644
index 000000000..2f76a6531
--- /dev/null
+++ b/test/packetimpact/tests/tcp_timewait_reset_test.go
@@ -0,0 +1,68 @@
+// Copyright 2020 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.
+
+package tcp_timewait_reset_test
+
+import (
+ "flag"
+ "testing"
+ "time"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/test/packetimpact/testbench"
+)
+
+func init() {
+ testbench.RegisterFlags(flag.CommandLine)
+}
+
+// TestTimeWaitReset tests handling of RST when in TIME_WAIT state.
+func TestTimeWaitReset(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ defer dut.TearDown()
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
+ defer dut.Close(t, listenFD)
+ conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(t)
+
+ conn.Connect(t)
+ acceptFD, _ := dut.Accept(t, listenFD)
+
+ // Trigger active close.
+ dut.Close(t, acceptFD)
+
+ _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
+ if err != nil {
+ t.Fatalf("expected a FIN: %s", err)
+ }
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ // Send a FIN, DUT should transition to TIME_WAIT from FIN_WAIT2.
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)})
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ t.Fatalf("expected an ACK for our FIN: %s", err)
+ }
+
+ // Send a RST, the DUT should transition to CLOSED from TIME_WAIT.
+ // This is the default Linux behavior, it can be changed to ignore RSTs via
+ // sysctl net.ipv4.tcp_rfc1337.
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)})
+
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ // The DUT should reply with RST to our ACK as the state should have
+ // transitioned to CLOSED.
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
+ t.Fatalf("expected a RST: %s", err)
+ }
+}
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index de753fc4e..e5d43cf2e 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -2430,6 +2430,7 @@ cc_library(
":socket_netlink_route_util",
":socket_test_util",
"//test/util:capability_util",
+ "//test/util:cleanup",
gtest,
],
alwayslink = 1,
diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc
index 46b6f38db..3aab25b23 100644
--- a/test/syscalls/linux/mount.cc
+++ b/test/syscalls/linux/mount.cc
@@ -147,8 +147,15 @@ TEST(MountTest, UmountDetach) {
// Unmount the tmpfs.
mount.Release()();
- const struct stat after2 = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path()));
- EXPECT_EQ(before.st_ino, after2.st_ino);
+ // Only check for inode number equality if the directory is not in overlayfs.
+ // If xino option is not enabled and if all overlayfs layers do not belong to
+ // the same filesystem then "the value of st_ino for directory objects may not
+ // be persistent and could change even while the overlay filesystem is
+ // mounted." -- Documentation/filesystems/overlayfs.txt
+ if (!ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) {
+ const struct stat after2 = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path()));
+ EXPECT_EQ(before.st_ino, after2.st_ino);
+ }
// Can still read file after unmounting.
std::vector<char> buf(sizeof(kContents));
@@ -213,8 +220,15 @@ TEST(MountTest, MountTmpfs) {
}
// Now that dir is unmounted again, we should have the old inode back.
- const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path()));
- EXPECT_EQ(before.st_ino, after.st_ino);
+ // Only check for inode number equality if the directory is not in overlayfs.
+ // If xino option is not enabled and if all overlayfs layers do not belong to
+ // the same filesystem then "the value of st_ino for directory objects may not
+ // be persistent and could change even while the overlay filesystem is
+ // mounted." -- Documentation/filesystems/overlayfs.txt
+ if (!ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) {
+ const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path()));
+ EXPECT_EQ(before.st_ino, after.st_ino);
+ }
}
TEST(MountTest, MountTmpfsMagicValIgnored) {
diff --git a/test/syscalls/linux/prctl.cc b/test/syscalls/linux/prctl.cc
index 04c5161f5..f675dc430 100644
--- a/test/syscalls/linux/prctl.cc
+++ b/test/syscalls/linux/prctl.cc
@@ -153,7 +153,7 @@ TEST(PrctlTest, PDeathSig) {
// Enable tracing, then raise SIGSTOP and expect our parent to suppress
// it.
TEST_CHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) >= 0);
- raise(SIGSTOP);
+ TEST_CHECK(raise(SIGSTOP) == 0);
// Sleep until killed by our parent death signal. sleep(3) is
// async-signal-safe, absl::SleepFor isn't.
while (true) {
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index b73189e55..1ba7bf4f5 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -694,6 +694,30 @@ TEST(ProcSelfFd, OpenFd) {
ASSERT_THAT(close(pipe_fds[1]), SyscallSucceeds());
}
+static void CheckFdDirGetdentsDuplicates(const std::string& path) {
+ const FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(path.c_str(), O_RDONLY | O_DIRECTORY));
+ // Open a FD whose value is supposed to be much larger than
+ // the number of FDs opened by current process.
+ auto newfd = fcntl(fd.get(), F_DUPFD, 1024);
+ EXPECT_GE(newfd, 1024);
+ auto fd_closer = Cleanup([newfd]() { close(newfd); });
+ auto fd_files = ASSERT_NO_ERRNO_AND_VALUE(ListDir(path.c_str(), false));
+ std::unordered_set<std::string> fd_files_dedup(fd_files.begin(),
+ fd_files.end());
+ EXPECT_EQ(fd_files.size(), fd_files_dedup.size());
+}
+
+// This is a regression test for gvisor.dev/issues/3894
+TEST(ProcSelfFd, GetdentsDuplicates) {
+ CheckFdDirGetdentsDuplicates("/proc/self/fd");
+}
+
+// This is a regression test for gvisor.dev/issues/3894
+TEST(ProcSelfFdInfo, GetdentsDuplicates) {
+ CheckFdDirGetdentsDuplicates("/proc/self/fdinfo");
+}
+
TEST(ProcSelfFdInfo, CorrectFds) {
// Make sure there is at least one open file.
auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
@@ -773,17 +797,12 @@ TEST(ProcCpuinfo, DeniesWriteNonRoot) {
constexpr int kNobody = 65534;
EXPECT_THAT(syscall(SYS_setuid, kNobody), SyscallSucceeds());
EXPECT_THAT(open("/proc/cpuinfo", O_WRONLY), SyscallFailsWithErrno(EACCES));
- // TODO(gvisor.dev/issue/1193): Properly support setting size attributes in
- // kernfs.
- if (!IsRunningOnGvisor() || IsRunningWithVFS1()) {
- EXPECT_THAT(truncate("/proc/cpuinfo", 123),
- SyscallFailsWithErrno(EACCES));
- }
+ EXPECT_THAT(truncate("/proc/cpuinfo", 123), SyscallFailsWithErrno(EACCES));
});
}
// With root privileges, it is possible to open /proc/cpuinfo with write mode,
-// but all write operations will return EIO.
+// but all write operations should fail.
TEST(ProcCpuinfo, DeniesWriteRoot) {
// VFS1 does not behave differently for root/non-root.
SKIP_IF(IsRunningWithVFS1());
@@ -792,16 +811,10 @@ TEST(ProcCpuinfo, DeniesWriteRoot) {
int fd;
EXPECT_THAT(fd = open("/proc/cpuinfo", O_WRONLY), SyscallSucceeds());
if (fd > 0) {
- EXPECT_THAT(write(fd, "x", 1), SyscallFailsWithErrno(EIO));
- EXPECT_THAT(pwrite(fd, "x", 1, 123), SyscallFailsWithErrno(EIO));
- }
- // TODO(gvisor.dev/issue/1193): Properly support setting size attributes in
- // kernfs.
- if (!IsRunningOnGvisor() || IsRunningWithVFS1()) {
- if (fd > 0) {
- EXPECT_THAT(ftruncate(fd, 123), SyscallFailsWithErrno(EIO));
- }
- EXPECT_THAT(truncate("/proc/cpuinfo", 123), SyscallFailsWithErrno(EIO));
+ // Truncate is not tested--it may succeed on some kernels without doing
+ // anything.
+ EXPECT_THAT(write(fd, "x", 1), SyscallFails());
+ EXPECT_THAT(pwrite(fd, "x", 1, 123), SyscallFails());
}
}
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc
index 79eb48afa..49a0f06d9 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc
@@ -15,10 +15,12 @@
#include "test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h"
#include <arpa/inet.h>
+#include <poll.h>
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_netlink_route_util.h"
#include "test/util/capability_util.h"
+#include "test/util/cleanup.h"
namespace gvisor {
namespace testing {
@@ -33,9 +35,23 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) {
// Add an IP address to the loopback interface.
Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
struct in_addr addr;
- EXPECT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr));
- EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET,
+ ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr));
+ ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET,
/*prefixlen=*/24, &addr, sizeof(addr)));
+ Cleanup defer_addr_removal = Cleanup(
+ [loopback_link = std::move(loopback_link), addr = std::move(addr)] {
+ if (IsRunningOnGvisor()) {
+ // TODO(gvisor.dev/issue/3921): Remove this once deleting addresses
+ // via netlink is supported.
+ EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET,
+ /*prefixlen=*/24, &addr, sizeof(addr)),
+ PosixErrorIs(EOPNOTSUPP, ::testing::_));
+ } else {
+ EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET,
+ /*prefixlen=*/24, &addr,
+ sizeof(addr)));
+ }
+ });
auto snd_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcv_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -45,10 +61,10 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) {
TestAddress sender_addr("V4NotAssignd1");
sender_addr.addr.ss_family = AF_INET;
sender_addr.addr_len = sizeof(sockaddr_in);
- EXPECT_EQ(1, inet_pton(AF_INET, "192.0.2.2",
+ ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.2",
&(reinterpret_cast<sockaddr_in*>(&sender_addr.addr)
->sin_addr.s_addr)));
- EXPECT_THAT(
+ ASSERT_THAT(
bind(snd_sock->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr),
sender_addr.addr_len),
SyscallSucceeds());
@@ -58,10 +74,10 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) {
TestAddress receiver_addr("V4NotAssigned2");
receiver_addr.addr.ss_family = AF_INET;
receiver_addr.addr_len = sizeof(sockaddr_in);
- EXPECT_EQ(1, inet_pton(AF_INET, "192.0.2.254",
+ ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.254",
&(reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)
->sin_addr.s_addr)));
- EXPECT_THAT(
+ ASSERT_THAT(
bind(rcv_sock->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
receiver_addr.addr_len),
SyscallSucceeds());
@@ -70,10 +86,10 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) {
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
&receiver_addr_len),
SyscallSucceeds());
- EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
+ ASSERT_EQ(receiver_addr_len, receiver_addr.addr_len);
char send_buf[kSendBufSize];
RandomizeBuffer(send_buf, kSendBufSize);
- EXPECT_THAT(
+ ASSERT_THAT(
RetryEINTR(sendto)(snd_sock->get(), send_buf, kSendBufSize, 0,
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
receiver_addr.addr_len),
@@ -83,7 +99,126 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) {
char recv_buf[kSendBufSize] = {};
ASSERT_THAT(RetryEINTR(recv)(rcv_sock->get(), recv_buf, kSendBufSize, 0),
SyscallSucceedsWithValue(kSendBufSize));
- EXPECT_EQ(0, memcmp(send_buf, recv_buf, kSendBufSize));
+ ASSERT_EQ(0, memcmp(send_buf, recv_buf, kSendBufSize));
+}
+
+// Tests that broadcast packets are delivered to all interested sockets
+// (wildcard and broadcast address specified sockets).
+//
+// Note, we cannot test the IPv4 Broadcast (255.255.255.255) because we do
+// not have a route to it.
+TEST_P(IPv4UDPUnboundSocketNetlinkTest, ReuseAddrSubnetDirectedBroadcast) {
+ constexpr uint16_t kPort = 9876;
+ // Wait up to 20 seconds for the data.
+ constexpr int kPollTimeoutMs = 20000;
+ // Number of sockets per socket type.
+ constexpr int kNumSocketsPerType = 2;
+
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+
+ // Add an IP address to the loopback interface.
+ Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
+ struct in_addr addr;
+ ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr));
+ ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET,
+ 24 /* prefixlen */, &addr, sizeof(addr)));
+ Cleanup defer_addr_removal = Cleanup(
+ [loopback_link = std::move(loopback_link), addr = std::move(addr)] {
+ if (IsRunningOnGvisor()) {
+ // TODO(gvisor.dev/issue/3921): Remove this once deleting addresses
+ // via netlink is supported.
+ EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET,
+ /*prefixlen=*/24, &addr, sizeof(addr)),
+ PosixErrorIs(EOPNOTSUPP, ::testing::_));
+ } else {
+ EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET,
+ /*prefixlen=*/24, &addr,
+ sizeof(addr)));
+ }
+ });
+
+ TestAddress broadcast_address("SubnetBroadcastAddress");
+ broadcast_address.addr.ss_family = AF_INET;
+ broadcast_address.addr_len = sizeof(sockaddr_in);
+ auto broadcast_address_in =
+ reinterpret_cast<sockaddr_in*>(&broadcast_address.addr);
+ ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.255",
+ &broadcast_address_in->sin_addr.s_addr));
+ broadcast_address_in->sin_port = htons(kPort);
+
+ TestAddress any_address = V4Any();
+ reinterpret_cast<sockaddr_in*>(&any_address.addr)->sin_port = htons(kPort);
+
+ // We create sockets bound to both the wildcard address and the broadcast
+ // address to make sure both of these types of "broadcast interested" sockets
+ // receive broadcast packets.
+ std::vector<std::unique_ptr<FileDescriptor>> socks;
+ for (bool bind_wildcard : {false, true}) {
+ // Create multiple sockets for each type of "broadcast interested"
+ // socket so we can test that all sockets receive the broadcast packet.
+ for (int i = 0; i < kNumSocketsPerType; i++) {
+ auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto idx = socks.size();
+
+ ASSERT_THAT(setsockopt(sock->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceedsWithValue(0))
+ << "socks[" << idx << "]";
+
+ ASSERT_THAT(setsockopt(sock->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceedsWithValue(0))
+ << "socks[" << idx << "]";
+
+ if (bind_wildcard) {
+ ASSERT_THAT(
+ bind(sock->get(), reinterpret_cast<sockaddr*>(&any_address.addr),
+ any_address.addr_len),
+ SyscallSucceeds())
+ << "socks[" << idx << "]";
+ } else {
+ ASSERT_THAT(bind(sock->get(),
+ reinterpret_cast<sockaddr*>(&broadcast_address.addr),
+ broadcast_address.addr_len),
+ SyscallSucceeds())
+ << "socks[" << idx << "]";
+ }
+
+ socks.push_back(std::move(sock));
+ }
+ }
+
+ char send_buf[kSendBufSize];
+ RandomizeBuffer(send_buf, kSendBufSize);
+
+ // Broadcasts from each socket should be received by every socket (including
+ // the sending socket).
+ for (int w = 0; w < socks.size(); w++) {
+ auto& w_sock = socks[w];
+ ASSERT_THAT(
+ RetryEINTR(sendto)(w_sock->get(), send_buf, kSendBufSize, 0,
+ reinterpret_cast<sockaddr*>(&broadcast_address.addr),
+ broadcast_address.addr_len),
+ SyscallSucceedsWithValue(kSendBufSize))
+ << "write socks[" << w << "]";
+
+ // Check that we received the packet on all sockets.
+ for (int r = 0; r < socks.size(); r++) {
+ auto& r_sock = socks[r];
+
+ struct pollfd poll_fd = {r_sock->get(), POLLIN, 0};
+ EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
+ SyscallSucceedsWithValue(1))
+ << "write socks[" << w << "] & read socks[" << r << "]";
+
+ char recv_buf[kSendBufSize] = {};
+ EXPECT_THAT(RetryEINTR(recv)(r_sock->get(), recv_buf, kSendBufSize, 0),
+ SyscallSucceedsWithValue(kSendBufSize))
+ << "write socks[" << w << "] & read socks[" << r << "]";
+ EXPECT_EQ(0, memcmp(send_buf, recv_buf, kSendBufSize))
+ << "write socks[" << w << "] & read socks[" << r << "]";
+ }
+ }
}
} // namespace testing
diff --git a/test/syscalls/linux/socket_netlink_route_util.cc b/test/syscalls/linux/socket_netlink_route_util.cc
index bde1dbb4d..a354f3f80 100644
--- a/test/syscalls/linux/socket_netlink_route_util.cc
+++ b/test/syscalls/linux/socket_netlink_route_util.cc
@@ -26,6 +26,62 @@ namespace {
constexpr uint32_t kSeq = 12345;
+// Types of address modifications that may be performed on an interface.
+enum class LinkAddrModification {
+ kAdd,
+ kDelete,
+};
+
+// Populates |hdr| with appripriate values for the modification type.
+PosixError PopulateNlmsghdr(LinkAddrModification modification,
+ struct nlmsghdr* hdr) {
+ switch (modification) {
+ case LinkAddrModification::kAdd:
+ hdr->nlmsg_type = RTM_NEWADDR;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ return NoError();
+ case LinkAddrModification::kDelete:
+ hdr->nlmsg_type = RTM_DELADDR;
+ hdr->nlmsg_flags = NLM_F_REQUEST;
+ return NoError();
+ }
+
+ return PosixError(EINVAL);
+}
+
+// Adds or removes the specified address from the specified interface.
+PosixError LinkModifyLocalAddr(int index, int family, int prefixlen,
+ const void* addr, int addrlen,
+ LinkAddrModification modification) {
+ ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE));
+
+ struct request {
+ struct nlmsghdr hdr;
+ struct ifaddrmsg ifaddr;
+ char attrbuf[512];
+ };
+
+ struct request req = {};
+ PosixError err = PopulateNlmsghdr(modification, &req.hdr);
+ if (!err.ok()) {
+ return err;
+ }
+ req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifaddr));
+ req.hdr.nlmsg_seq = kSeq;
+ req.ifaddr.ifa_index = index;
+ req.ifaddr.ifa_family = family;
+ req.ifaddr.ifa_prefixlen = prefixlen;
+
+ struct rtattr* rta = reinterpret_cast<struct rtattr*>(
+ reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len));
+ rta->rta_type = IFA_LOCAL;
+ rta->rta_len = RTA_LENGTH(addrlen);
+ req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen);
+ memcpy(RTA_DATA(rta), addr, addrlen);
+
+ return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len);
+}
+
} // namespace
PosixError DumpLinks(
@@ -84,31 +140,14 @@ PosixErrorOr<Link> LoopbackLink() {
PosixError LinkAddLocalAddr(int index, int family, int prefixlen,
const void* addr, int addrlen) {
- ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE));
-
- struct request {
- struct nlmsghdr hdr;
- struct ifaddrmsg ifaddr;
- char attrbuf[512];
- };
-
- struct request req = {};
- req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifaddr));
- req.hdr.nlmsg_type = RTM_NEWADDR;
- req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
- req.hdr.nlmsg_seq = kSeq;
- req.ifaddr.ifa_index = index;
- req.ifaddr.ifa_family = family;
- req.ifaddr.ifa_prefixlen = prefixlen;
-
- struct rtattr* rta = reinterpret_cast<struct rtattr*>(
- reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len));
- rta->rta_type = IFA_LOCAL;
- rta->rta_len = RTA_LENGTH(addrlen);
- req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen);
- memcpy(RTA_DATA(rta), addr, addrlen);
+ return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen,
+ LinkAddrModification::kAdd);
+}
- return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len);
+PosixError LinkDelLocalAddr(int index, int family, int prefixlen,
+ const void* addr, int addrlen) {
+ return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen,
+ LinkAddrModification::kDelete);
}
PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change) {
diff --git a/test/syscalls/linux/socket_netlink_route_util.h b/test/syscalls/linux/socket_netlink_route_util.h
index 149c4a7f6..e5badca70 100644
--- a/test/syscalls/linux/socket_netlink_route_util.h
+++ b/test/syscalls/linux/socket_netlink_route_util.h
@@ -43,6 +43,10 @@ PosixErrorOr<Link> LoopbackLink();
PosixError LinkAddLocalAddr(int index, int family, int prefixlen,
const void* addr, int addrlen);
+// LinkDelLocalAddr removes IFA_LOCAL attribute on the interface.
+PosixError LinkDelLocalAddr(int index, int family, int prefixlen,
+ const void* addr, int addrlen);
+
// LinkChangeFlags changes interface flags. E.g. IFF_UP.
PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change);
diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc
index 2503960f3..1b2941c37 100644
--- a/test/syscalls/linux/stat.cc
+++ b/test/syscalls/linux/stat.cc
@@ -97,6 +97,11 @@ TEST_F(StatTest, FstatatSymlink) {
}
TEST_F(StatTest, Nlinks) {
+ // Skip this test if we are testing overlayfs because overlayfs does not
+ // (intentionally) return the correct nlink value for directories.
+ // See fs/overlayfs/inode.c:ovl_getattr().
+ SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir())));
+
TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
// Directory is initially empty, it should contain 2 links (one from itself,
@@ -432,6 +437,11 @@ TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoRandomSave) {
// Test link counts with a directory as the child.
TEST_F(StatTest, LinkCountsWithDirChild) {
+ // Skip this test if we are testing overlayfs because overlayfs does not
+ // (intentionally) return the correct nlink value for directories.
+ // See fs/overlayfs/inode.c:ovl_getattr().
+ SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir())));
+
const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
// Before a child is added the two links are "." and the link from the parent.
diff --git a/test/syscalls/linux/statfs.cc b/test/syscalls/linux/statfs.cc
index 99ab280fd..f0fb166bd 100644
--- a/test/syscalls/linux/statfs.cc
+++ b/test/syscalls/linux/statfs.cc
@@ -27,10 +27,6 @@ namespace testing {
namespace {
-// From linux/magic.h. For some reason, not defined in the headers for some
-// build environments.
-#define OVERLAYFS_SUPER_MAGIC 0x794c7630
-
TEST(StatfsTest, CannotStatBadPath) {
auto temp_file = NewTempAbsPathInDir("/tmp");
diff --git a/test/syscalls/linux/xattr.cc b/test/syscalls/linux/xattr.cc
index 1a1010bb5..bd3f829c4 100644
--- a/test/syscalls/linux/xattr.cc
+++ b/test/syscalls/linux/xattr.cc
@@ -615,12 +615,18 @@ TEST_F(XattrTest, TrustedNamespaceWithCapSysAdmin) {
SKIP_IF(IsRunningOnGvisor() &&
!ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(test_file_name_)));
- // Setting/Getting in the trusted namespace requires CAP_SYS_ADMIN.
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
-
const char* path = test_file_name_.c_str();
const char name[] = "trusted.test";
+ // Writing to the trusted.* xattr namespace requires CAP_SYS_ADMIN in the root
+ // user namespace. There's no easy way to check that, other than trying the
+ // operation and seeing what happens. We'll call removexattr because it's
+ // simplest.
+ if (removexattr(path, name) < 0) {
+ SKIP_IF(errno == EPERM);
+ FAIL() << "unexpected errno from removexattr: " << errno;
+ }
+
// Set.
char val = 'a';
size_t size = sizeof(val);
diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc
index 572675622..b16055dd8 100644
--- a/test/util/fs_util.cc
+++ b/test/util/fs_util.cc
@@ -649,5 +649,19 @@ PosixErrorOr<bool> IsTmpfs(const std::string& path) {
}
#endif // __linux__
+PosixErrorOr<bool> IsOverlayfs(const std::string& path) {
+ struct statfs stat;
+ if (statfs(path.c_str(), &stat)) {
+ if (errno == ENOENT) {
+ // Nothing at path, don't raise this as an error. Instead, just report no
+ // overlayfs at path.
+ return false;
+ }
+ return PosixError(errno,
+ absl::StrFormat("statfs(\"%s\", %#p)", path, &stat));
+ }
+ return stat.f_type == OVERLAYFS_SUPER_MAGIC;
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/util/fs_util.h b/test/util/fs_util.h
index 314637de0..c99cf5eb7 100644
--- a/test/util/fs_util.h
+++ b/test/util/fs_util.h
@@ -38,6 +38,10 @@ constexpr int kOLargeFile = 00400000;
#error "Unknown architecture"
#endif
+// From linux/magic.h. For some reason, not defined in the headers for some
+// build environments.
+#define OVERLAYFS_SUPER_MAGIC 0x794c7630
+
// Returns a status or the current working directory.
PosixErrorOr<std::string> GetCWD();
@@ -184,6 +188,9 @@ PosixErrorOr<std::string> ProcessExePath(int pid);
PosixErrorOr<bool> IsTmpfs(const std::string& path);
#endif // __linux__
+// IsOverlayfs returns true if the file at path is backed by overlayfs.
+PosixErrorOr<bool> IsOverlayfs(const std::string& path);
+
namespace internal {
// Not part of the public API.
std::string JoinPathImpl(std::initializer_list<absl::string_view> paths);