summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/benchmarks/fs/bazel_test.go1
-rw-r--r--test/benchmarks/harness/machine.go7
-rw-r--r--test/benchmarks/network/BUILD5
-rw-r--r--test/benchmarks/network/httpd_test.go276
-rw-r--r--test/benchmarks/network/iperf_test.go4
-rw-r--r--test/runner/defs.bzl20
-rw-r--r--test/runner/runner.go10
-rw-r--r--test/syscalls/BUILD1
-rw-r--r--test/syscalls/linux/dev.cc2
-rw-r--r--test/syscalls/linux/packet_socket.cc13
-rw-r--r--test/syscalls/linux/packet_socket_raw.cc58
-rw-r--r--test/syscalls/linux/socket_netdevice.cc23
-rw-r--r--test/util/test_util.cc6
-rw-r--r--test/util/test_util.h1
14 files changed, 416 insertions, 11 deletions
diff --git a/test/benchmarks/fs/bazel_test.go b/test/benchmarks/fs/bazel_test.go
index aabcdbd87..b7915e19d 100644
--- a/test/benchmarks/fs/bazel_test.go
+++ b/test/benchmarks/fs/bazel_test.go
@@ -32,6 +32,7 @@ func BenchmarkABSL(b *testing.B) {
if err != nil {
b.Fatalf("failed to get machine: %v", err)
}
+ defer machine.CleanUp()
// Dimensions here are clean/dirty cache (do or don't drop caches)
// and if the mount on which we are compiling is a tmpfs/bind mount.
diff --git a/test/benchmarks/harness/machine.go b/test/benchmarks/harness/machine.go
index 032b387fc..93c0db9ce 100644
--- a/test/benchmarks/harness/machine.go
+++ b/test/benchmarks/harness/machine.go
@@ -33,6 +33,9 @@ type Machine interface {
// Returns IP Address for the machine.
IPAddress() (net.IP, error)
+
+ // CleanUp cleans up this machine.
+ CleanUp()
}
// localMachine describes this machine.
@@ -62,3 +65,7 @@ func (l *localMachine) IPAddress() (net.IP, error) {
addr := conn.LocalAddr().(*net.UDPAddr)
return addr.IP, nil
}
+
+// CleanUp implements Machine.CleanUp and does nothing for localMachine.
+func (*localMachine) CleanUp() {
+}
diff --git a/test/benchmarks/network/BUILD b/test/benchmarks/network/BUILD
index 57328456d..ea78416cf 100644
--- a/test/benchmarks/network/BUILD
+++ b/test/benchmarks/network/BUILD
@@ -11,7 +11,10 @@ go_library(
go_test(
name = "network_test",
size = "large",
- srcs = ["iperf_test.go"],
+ srcs = [
+ "httpd_test.go",
+ "iperf_test.go",
+ ],
library = ":network",
tags = [
# Requires docker and runsc to be configured before test runs.
diff --git a/test/benchmarks/network/httpd_test.go b/test/benchmarks/network/httpd_test.go
new file mode 100644
index 000000000..f9afdf15f
--- /dev/null
+++ b/test/benchmarks/network/httpd_test.go
@@ -0,0 +1,276 @@
+// 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 network
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+ "strconv"
+ "testing"
+
+ "gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/test/benchmarks/harness"
+)
+
+// see Dockerfile '//images/benchmarks/httpd'.
+var docs = map[string]string{
+ "notfound": "notfound",
+ "1Kb": "latin1k.txt",
+ "10Kb": "latin10k.txt",
+ "100Kb": "latin100k.txt",
+ "1000Kb": "latin1000k.txt",
+ "1Mb": "latin1024k.txt",
+ "10Mb": "latin10240k.txt",
+}
+
+// BenchmarkHttpdConcurrency iterates the concurrency argument and tests
+// how well the runtime under test handles requests in parallel.
+func BenchmarkHttpdConcurrency(b *testing.B) {
+ // Grab a machine for the client and server.
+ clientMachine, err := h.GetMachine()
+ if err != nil {
+ b.Fatalf("failed to get client: %v", err)
+ }
+ defer clientMachine.CleanUp()
+
+ serverMachine, err := h.GetMachine()
+ if err != nil {
+ b.Fatalf("failed to get server: %v", err)
+ }
+ defer serverMachine.CleanUp()
+
+ // The test iterates over client concurrency, so set other parameters.
+ requests := 1000
+ concurrency := []int{1, 5, 10, 25}
+ doc := docs["10Kb"]
+
+ for _, c := range concurrency {
+ b.Run(fmt.Sprintf("%dConcurrency", c), func(b *testing.B) {
+ runHttpd(b, clientMachine, serverMachine, doc, requests, c)
+ })
+ }
+}
+
+// BenchmarkHttpdDocSize iterates over different sized payloads, testing how
+// well the runtime handles different payload sizes.
+func BenchmarkHttpdDocSize(b *testing.B) {
+ clientMachine, err := h.GetMachine()
+ if err != nil {
+ b.Fatalf("failed to get machine: %v", err)
+ }
+ defer clientMachine.CleanUp()
+
+ serverMachine, err := h.GetMachine()
+ if err != nil {
+ b.Fatalf("failed to get machine: %v", err)
+ }
+ defer serverMachine.CleanUp()
+
+ requests := 1000
+ concurrency := 1
+
+ for name, filename := range docs {
+ b.Run(name, func(b *testing.B) {
+ runHttpd(b, clientMachine, serverMachine, filename, requests, concurrency)
+ })
+ }
+}
+
+// runHttpd runs a single test run.
+func runHttpd(b *testing.B, clientMachine, serverMachine harness.Machine, doc string, requests, concurrency int) {
+ b.Helper()
+
+ // Grab a container from the server.
+ ctx := context.Background()
+ server := serverMachine.GetContainer(ctx, b)
+ defer server.CleanUp(ctx)
+
+ // Copy the docs to /tmp and serve from there.
+ cmd := "mkdir -p /tmp/html; cp -r /local /tmp/html/.; apache2 -X"
+ port := 80
+
+ // Start the server.
+ server.Spawn(ctx, dockerutil.RunOpts{
+ Image: "benchmarks/httpd",
+ Ports: []int{port},
+ Env: []string{
+ // Standard environmental variables for httpd.
+ "APACHE_RUN_DIR=/tmp",
+ "APACHE_RUN_USER=nobody",
+ "APACHE_RUN_GROUP=nogroup",
+ "APACHE_LOG_DIR=/tmp",
+ "APACHE_PID_FILE=/tmp/apache.pid",
+ },
+ }, "sh", "-c", cmd)
+
+ ip, err := serverMachine.IPAddress()
+ if err != nil {
+ b.Fatalf("failed to find server ip: %v", err)
+ }
+
+ servingPort, err := server.FindPort(ctx, port)
+ if err != nil {
+ b.Fatalf("failed to find server port %d: %v", port, err)
+ }
+
+ // Check the server is serving.
+ harness.WaitUntilServing(ctx, clientMachine, ip, servingPort)
+
+ // Grab a client.
+ client := clientMachine.GetContainer(ctx, b)
+ defer client.CleanUp(ctx)
+
+ path := fmt.Sprintf("http://%s:%d/%s", ip, servingPort, doc)
+ // See apachebench (ab) for flags.
+ cmd = fmt.Sprintf("ab -n %d -c %d %s", requests, concurrency, path)
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ out, err := client.Run(ctx, dockerutil.RunOpts{
+ Image: "benchmarks/ab",
+ }, "sh", "-c", cmd)
+ if err != nil {
+ b.Fatalf("run failed with: %v", err)
+ }
+
+ b.StopTimer()
+
+ // Parse and report custom metrics.
+ transferRate, err := parseTransferRate(out)
+ if err != nil {
+ b.Logf("failed to parse transferrate: %v", err)
+ }
+ b.ReportMetric(transferRate*1024, "transfer_rate") // Convert from Kb/s to b/s.
+
+ latency, err := parseLatency(out)
+ if err != nil {
+ b.Logf("failed to parse latency: %v", err)
+ }
+ b.ReportMetric(latency/1000, "mean_latency") // Convert from ms to s.
+
+ reqPerSecond, err := parseRequestsPerSecond(out)
+ if err != nil {
+ b.Logf("failed to parse requests per second: %v", err)
+ }
+ b.ReportMetric(reqPerSecond, "requests_per_second")
+
+ b.StartTimer()
+ }
+}
+
+var transferRateRE = regexp.MustCompile(`Transfer rate:\s+(\d+\.?\d+?)\s+\[Kbytes/sec\]\s+received`)
+
+// parseTransferRate parses transfer rate from apachebench output.
+func parseTransferRate(data string) (float64, error) {
+ match := transferRateRE.FindStringSubmatch(data)
+ if len(match) < 2 {
+ return 0, fmt.Errorf("failed get bandwidth: %s", data)
+ }
+ return strconv.ParseFloat(match[1], 64)
+}
+
+var latencyRE = regexp.MustCompile(`Total:\s+\d+\s+(\d+)\s+(\d+\.?\d+?)\s+\d+\s+\d+\s`)
+
+// parseLatency parses latency from apachebench output.
+func parseLatency(data string) (float64, error) {
+ match := latencyRE.FindStringSubmatch(data)
+ if len(match) < 2 {
+ return 0, fmt.Errorf("failed get bandwidth: %s", data)
+ }
+ return strconv.ParseFloat(match[1], 64)
+}
+
+var requestsPerSecondRE = regexp.MustCompile(`Requests per second:\s+(\d+\.?\d+?)\s+`)
+
+// parseRequestsPerSecond parses requests per second from apachebench output.
+func parseRequestsPerSecond(data string) (float64, error) {
+ match := requestsPerSecondRE.FindStringSubmatch(data)
+ if len(match) < 2 {
+ return 0, fmt.Errorf("failed get bandwidth: %s", data)
+ }
+ return strconv.ParseFloat(match[1], 64)
+}
+
+// Sample output from apachebench.
+const sampleData = `This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
+Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
+Licensed to The Apache Software Foundation, http://www.apache.org/
+
+Benchmarking 10.10.10.10 (be patient).....done
+
+
+Server Software: Apache/2.4.38
+Server Hostname: 10.10.10.10
+Server Port: 80
+
+Document Path: /latin10k.txt
+Document Length: 210 bytes
+
+Concurrency Level: 1
+Time taken for tests: 0.180 seconds
+Complete requests: 100
+Failed requests: 0
+Non-2xx responses: 100
+Total transferred: 38800 bytes
+HTML transferred: 21000 bytes
+Requests per second: 556.44 [#/sec] (mean)
+Time per request: 1.797 [ms] (mean)
+Time per request: 1.797 [ms] (mean, across all concurrent requests)
+Transfer rate: 210.84 [Kbytes/sec] received
+
+Connection Times (ms)
+ min mean[+/-sd] median max
+Connect: 0 0 0.2 0 2
+Processing: 1 2 1.0 1 8
+Waiting: 1 1 1.0 1 7
+Total: 1 2 1.2 1 10
+
+Percentage of the requests served within a certain time (ms)
+ 50% 1
+ 66% 2
+ 75% 2
+ 80% 2
+ 90% 2
+ 95% 3
+ 98% 7
+ 99% 10
+ 100% 10 (longest request)`
+
+// TestParsers checks the parsers work.
+func TestParsers(t *testing.T) {
+ want := 210.84
+ got, err := parseTransferRate(sampleData)
+ if err != nil {
+ t.Fatalf("failed to parse transfer rate with error: %v", err)
+ } else if got != want {
+ t.Fatalf("parseTransferRate got: %f, want: %f", got, want)
+ }
+
+ want = 2.0
+ got, err = parseLatency(sampleData)
+ if err != nil {
+ t.Fatalf("failed to parse transfer rate with error: %v", err)
+ } else if got != want {
+ t.Fatalf("parseLatency got: %f, want: %f", got, want)
+ }
+
+ want = 556.44
+ got, err = parseRequestsPerSecond(sampleData)
+ if err != nil {
+ t.Fatalf("failed to parse transfer rate with error: %v", err)
+ } else if got != want {
+ t.Fatalf("parseRequestsPerSecond got: %f, want: %f", got, want)
+ }
+}
diff --git a/test/benchmarks/network/iperf_test.go b/test/benchmarks/network/iperf_test.go
index 72e9c99a8..48cc9dd8f 100644
--- a/test/benchmarks/network/iperf_test.go
+++ b/test/benchmarks/network/iperf_test.go
@@ -35,11 +35,13 @@ func BenchmarkIperf(b *testing.B) {
if err != nil {
b.Fatalf("failed to get machine: %v", err)
}
+ defer clientMachine.CleanUp()
serverMachine, err := h.GetMachine()
if err != nil {
b.Fatalf("failed to get machine: %v", err)
}
+ defer serverMachine.CleanUp()
for _, bm := range []struct {
name string
@@ -111,7 +113,7 @@ func BenchmarkIperf(b *testing.B) {
if err != nil {
b.Fatalf("failed to parse bandwitdth from %s: %v", out, err)
}
- b.ReportMetric(bW, "KBytes/sec")
+ b.ReportMetric(bW*1024, "bandwidth") // Convert from Kb/s to b/s.
b.StartTimer()
}
})
diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl
index 921e499be..600cb5192 100644
--- a/test/runner/defs.bzl
+++ b/test/runner/defs.bzl
@@ -61,7 +61,8 @@ def _syscall_test(
file_access = "exclusive",
overlay = False,
add_uds_tree = False,
- vfs2 = False):
+ vfs2 = False,
+ fuse = False):
# Prepend "runsc" to non-native platform names.
full_platform = platform if platform == "native" else "runsc_" + platform
@@ -73,6 +74,8 @@ def _syscall_test(
name += "_overlay"
if vfs2:
name += "_vfs2"
+ if fuse:
+ name += "_fuse"
if network != "none":
name += "_" + network + "net"
@@ -107,6 +110,7 @@ def _syscall_test(
"--overlay=" + str(overlay),
"--add-uds-tree=" + str(add_uds_tree),
"--vfs2=" + str(vfs2),
+ "--fuse=" + str(fuse),
]
# Call the rule above.
@@ -129,6 +133,7 @@ def syscall_test(
add_uds_tree = False,
add_hostinet = False,
vfs2 = False,
+ fuse = False,
tags = None):
"""syscall_test is a macro that will create targets for all platforms.
@@ -188,6 +193,19 @@ def syscall_test(
vfs2 = True,
)
+ if vfs2 and fuse:
+ _syscall_test(
+ test = test,
+ shard_count = shard_count,
+ size = size,
+ platform = default_platform,
+ use_tmpfs = use_tmpfs,
+ add_uds_tree = add_uds_tree,
+ tags = platforms[default_platform] + vfs2_tags,
+ vfs2 = True,
+ fuse = True,
+ )
+
# TODO(gvisor.dev/issue/1487): Enable VFS2 overlay tests.
if add_overlay:
_syscall_test(
diff --git a/test/runner/runner.go b/test/runner/runner.go
index 5456e46a6..2296f3a46 100644
--- a/test/runner/runner.go
+++ b/test/runner/runner.go
@@ -47,6 +47,7 @@ var (
fileAccess = flag.String("file-access", "exclusive", "mounts root in exclusive or shared mode")
overlay = flag.Bool("overlay", false, "wrap filesystem mounts with writable tmpfs overlay")
vfs2 = flag.Bool("vfs2", false, "enable VFS2")
+ fuse = flag.Bool("fuse", false, "enable FUSE")
parallel = flag.Bool("parallel", false, "run tests in parallel")
runscPath = flag.String("runsc", "", "path to runsc binary")
@@ -149,6 +150,9 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
}
if *vfs2 {
args = append(args, "-vfs2")
+ if *fuse {
+ args = append(args, "-fuse")
+ }
}
if *debug {
args = append(args, "-debug", "-log-packets=true")
@@ -358,6 +362,12 @@ func runTestCaseRunsc(testBin string, tc gtest.TestCase, t *testing.T) {
vfsVar := "GVISOR_VFS"
if *vfs2 {
env = append(env, vfsVar+"=VFS2")
+ fuseVar := "FUSE_ENABLED"
+ if *fuse {
+ env = append(env, fuseVar+"=TRUE")
+ } else {
+ env = append(env, fuseVar+"=FALSE")
+ }
} else {
env = append(env, vfsVar+"=VFS1")
}
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index 28ef55945..c06a75ada 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -146,6 +146,7 @@ syscall_test(
)
syscall_test(
+ fuse = "True",
test = "//test/syscalls/linux:dev_test",
vfs2 = "True",
)
diff --git a/test/syscalls/linux/dev.cc b/test/syscalls/linux/dev.cc
index 3c88c4cbd..6fa16208e 100644
--- a/test/syscalls/linux/dev.cc
+++ b/test/syscalls/linux/dev.cc
@@ -156,7 +156,7 @@ TEST(DevTest, TTYExists) {
TEST(DevTest, OpenDevFuse) {
// Note(gvisor.dev/issue/3076) This won't work in the sentry until the new
// device registration is complete.
- SKIP_IF(IsRunningWithVFS1() || IsRunningOnGvisor());
+ SKIP_IF(IsRunningWithVFS1() || IsRunningOnGvisor() || !IsFUSEEnabled());
ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/fuse", O_RDONLY));
}
diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc
index 5ac68feb4..ce63adb23 100644
--- a/test/syscalls/linux/packet_socket.cc
+++ b/test/syscalls/linux/packet_socket.cc
@@ -343,7 +343,7 @@ TEST_P(CookedPacketTest, BindReceive) {
}
// Double Bind socket.
-TEST_P(CookedPacketTest, DoubleBind) {
+TEST_P(CookedPacketTest, DoubleBindSucceeds) {
struct sockaddr_ll bind_addr = {};
bind_addr.sll_family = AF_PACKET;
bind_addr.sll_protocol = htons(GetParam());
@@ -354,12 +354,11 @@ TEST_P(CookedPacketTest, DoubleBind) {
SyscallSucceeds());
// Binding socket again should fail.
- ASSERT_THAT(
- bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- // Linux 4.09 returns EINVAL here, but some time before 4.19 it switched
- // to EADDRINUSE.
- AnyOf(SyscallFailsWithErrno(EADDRINUSE), SyscallFailsWithErrno(EINVAL)));
+ ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ // Linux 4.09 returns EINVAL here, but some time before 4.19 it
+ // switched to EADDRINUSE.
+ SyscallSucceeds());
}
// Bind and verify we do not receive data on interface which is not bound
diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc
index 6a963b12c..e44062475 100644
--- a/test/syscalls/linux/packet_socket_raw.cc
+++ b/test/syscalls/linux/packet_socket_raw.cc
@@ -559,6 +559,64 @@ TEST_P(RawPacketTest, SetSocketSendBuf) {
ASSERT_EQ(quarter_sz, val);
}
+TEST_P(RawPacketTest, GetSocketError) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ int val = 0;
+ socklen_t val_len = sizeof(val);
+ ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len),
+ SyscallSucceeds());
+ ASSERT_EQ(val, 0);
+}
+
+TEST_P(RawPacketTest, GetSocketErrorBind) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ {
+ // Bind to the loopback device.
+ struct sockaddr_ll bind_addr = {};
+ bind_addr.sll_family = AF_PACKET;
+ bind_addr.sll_protocol = htons(GetParam());
+ bind_addr.sll_ifindex = GetLoopbackIndex();
+
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // SO_ERROR should return no errors.
+ int val = 0;
+ socklen_t val_len = sizeof(val);
+ ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len),
+ SyscallSucceeds());
+ ASSERT_EQ(val, 0);
+ }
+
+ {
+ // Now try binding to an invalid interface.
+ struct sockaddr_ll bind_addr = {};
+ bind_addr.sll_family = AF_PACKET;
+ bind_addr.sll_protocol = htons(GetParam());
+ bind_addr.sll_ifindex = 0xffff; // Just pick a really large number.
+
+ // Binding should fail with EINVAL
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallFailsWithErrno(ENODEV));
+
+ // SO_ERROR does not return error when the device is invalid.
+ // On Linux there is just one odd ball condition where this can return
+ // an error where the device was valid and then removed or disabled
+ // between the first check for index and the actual registration of
+ // the packet endpoint. On Netstack this is not possible as the stack
+ // global mutex is held during registration and check.
+ int val = 0;
+ socklen_t val_len = sizeof(val);
+ ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len),
+ SyscallSucceeds());
+ ASSERT_EQ(val, 0);
+ }
+}
+
#ifndef __fuchsia__
TEST_P(RawPacketTest, SetSocketDetachFilterNoInstalledFilter) {
diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc
index 15d4b85a7..5f8d7f981 100644
--- a/test/syscalls/linux/socket_netdevice.cc
+++ b/test/syscalls/linux/socket_netdevice.cc
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <linux/ethtool.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
@@ -49,6 +50,7 @@ TEST(NetdeviceTest, Loopback) {
// Check that the loopback is zero hardware address.
ASSERT_THAT(ioctl(sock.get(), SIOCGIFHWADDR, &ifr), SyscallSucceeds());
+ EXPECT_EQ(ifr.ifr_hwaddr.sa_family, ARPHRD_LOOPBACK);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[0], 0);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[1], 0);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[2], 0);
@@ -178,6 +180,27 @@ TEST(NetdeviceTest, InterfaceMTU) {
EXPECT_GT(ifr.ifr_mtu, 0);
}
+TEST(NetdeviceTest, EthtoolGetTSInfo) {
+ FileDescriptor sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+
+ struct ethtool_ts_info tsi = {};
+ tsi.cmd = ETHTOOL_GET_TS_INFO; // Get NIC's Timestamping capabilities.
+
+ // Prepare the request.
+ struct ifreq ifr = {};
+ snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
+ ifr.ifr_data = (void*)&tsi;
+
+ // Check that SIOCGIFMTU returns a nonzero MTU.
+ if (IsRunningOnGvisor()) {
+ ASSERT_THAT(ioctl(sock.get(), SIOCETHTOOL, &ifr),
+ SyscallFailsWithErrno(EOPNOTSUPP));
+ return;
+ }
+ ASSERT_THAT(ioctl(sock.get(), SIOCETHTOOL, &ifr), SyscallSucceeds());
+}
+
} // namespace
} // namespace testing
diff --git a/test/util/test_util.cc b/test/util/test_util.cc
index 8a037f45f..d0c1d6426 100644
--- a/test/util/test_util.cc
+++ b/test/util/test_util.cc
@@ -42,6 +42,7 @@ namespace testing {
constexpr char kGvisorNetwork[] = "GVISOR_NETWORK";
constexpr char kGvisorVfs[] = "GVISOR_VFS";
+constexpr char kFuseEnabled[] = "FUSE_ENABLED";
bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; }
@@ -68,6 +69,11 @@ bool IsRunningWithVFS1() {
return strcmp(env, "VFS1") == 0;
}
+bool IsFUSEEnabled() {
+ const char* env = getenv(kFuseEnabled);
+ return env && strcmp(env, "TRUE") == 0;
+}
+
// Inline cpuid instruction. Preserve %ebx/%rbx register. In PIC compilations
// %ebx contains the address of the global offset table. %rbx is occasionally
// used to address stack variables in presence of dynamic allocas.
diff --git a/test/util/test_util.h b/test/util/test_util.h
index 0f9781038..373c54f32 100644
--- a/test/util/test_util.h
+++ b/test/util/test_util.h
@@ -225,6 +225,7 @@ const std::string GvisorPlatform();
bool IsRunningWithHostinet();
// TODO(gvisor.dev/issue/1624): Delete once VFS1 is gone.
bool IsRunningWithVFS1();
+bool IsFUSEEnabled();
#ifdef __linux__
void SetupGvisorDeathTest();