summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/benchmarks/base/startup_test.go12
-rw-r--r--test/benchmarks/tcp/tcp_proxy.go8
-rw-r--r--test/benchmarks/tools/fio_test.go2
-rw-r--r--test/packetimpact/README.md8
-rw-r--r--test/packetimpact/dut/posix_server.cc2
-rw-r--r--test/packetimpact/runner/dut.go26
-rw-r--r--test/packetimpact/tests/tcp_listen_backlog_test.go310
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go2
-rw-r--r--test/perf/BUILD7
-rw-r--r--test/perf/linux/BUILD20
-rw-r--r--test/perf/linux/randread_benchmark.cc2
-rw-r--r--test/perf/linux/verity_stat_benchmark.cc84
-rw-r--r--test/runner/gtest/gtest.go21
-rw-r--r--test/runner/main.go1
-rw-r--r--test/runtimes/runner/lib/BUILD6
-rw-r--r--test/runtimes/runner/lib/go_test_dependency_go118.go27
-rw-r--r--test/runtimes/runner/lib/go_test_dependency_not_go118.go25
-rw-r--r--test/runtimes/runner/lib/lib.go22
-rw-r--r--test/syscalls/BUILD14
-rw-r--r--test/syscalls/linux/BUILD102
-rw-r--r--test/syscalls/linux/cgroup.cc17
-rw-r--r--test/syscalls/linux/epoll.cc67
-rw-r--r--test/syscalls/linux/eventfd.cc25
-rw-r--r--test/syscalls/linux/inotify.cc28
-rw-r--r--test/syscalls/linux/ip_socket_test_util.cc105
-rw-r--r--test/syscalls/linux/ip_socket_test_util.h26
-rw-r--r--test/syscalls/linux/link.cc3
-rw-r--r--test/syscalls/linux/mmap.cc43
-rw-r--r--test/syscalls/linux/msgqueue.cc291
-rw-r--r--test/syscalls/linux/network_namespace.cc19
-rw-r--r--test/syscalls/linux/packet_socket.cc727
-rw-r--r--test/syscalls/linux/packet_socket_dgram.cc550
-rw-r--r--test/syscalls/linux/packet_socket_raw.cc59
-rw-r--r--test/syscalls/linux/ping_socket.cc16
-rw-r--r--test/syscalls/linux/pipe.cc24
-rw-r--r--test/syscalls/linux/proc.cc49
-rw-r--r--test/syscalls/linux/proc_isolated.cc100
-rw-r--r--test/syscalls/linux/proc_net.cc78
-rw-r--r--test/syscalls/linux/pty.cc88
-rw-r--r--test/syscalls/linux/raw_socket.cc242
-rw-r--r--test/syscalls/linux/raw_socket_hdrincl.cc5
-rw-r--r--test/syscalls/linux/raw_socket_icmp.cc27
-rw-r--r--test/syscalls/linux/sendfile.cc100
-rw-r--r--test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc59
-rw-r--r--test/syscalls/linux/socket_ip_udp_unbound_external_networking.h18
-rw-r--r--test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc299
-rw-r--r--test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h31
-rw-r--r--test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc28
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound.cc325
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc220
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h18
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc4
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound.cc80
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc4
-rw-r--r--test/syscalls/linux/socket_unix.cc15
-rw-r--r--test/syscalls/linux/socket_unix_stream.cc15
-rw-r--r--test/syscalls/linux/splice.cc103
-rw-r--r--test/syscalls/linux/tcp_socket.cc183
-rw-r--r--test/syscalls/linux/tuntap.cc210
-rw-r--r--test/syscalls/linux/udp_socket.cc205
-rw-r--r--test/util/BUILD27
-rw-r--r--test/util/capability_util.h112
-rw-r--r--test/util/cgroup_util.cc18
-rw-r--r--test/util/cgroup_util.h12
-rw-r--r--test/util/fs_util.cc3
-rw-r--r--test/util/fuchsia_capability_util.cc73
-rw-r--r--test/util/linux_capability_util.cc (renamed from test/util/capability_util.cc)10
-rw-r--r--test/util/linux_capability_util.h128
-rw-r--r--test/util/socket_util.cc12
-rw-r--r--test/util/socket_util.h12
70 files changed, 3732 insertions, 1882 deletions
diff --git a/test/benchmarks/base/startup_test.go b/test/benchmarks/base/startup_test.go
index 05a43ad17..197241622 100644
--- a/test/benchmarks/base/startup_test.go
+++ b/test/benchmarks/base/startup_test.go
@@ -34,15 +34,19 @@ func BenchmarkStartupEmpty(b *testing.B) {
defer machine.CleanUp()
ctx := context.Background()
+ b.StopTimer()
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
harness.DebugLog(b, "Running container: %d", i)
container := machine.GetContainer(ctx, b)
- defer container.CleanUp(ctx)
- if _, err := container.Run(ctx, dockerutil.RunOpts{
+ b.StartTimer()
+ if err := container.Spawn(ctx, dockerutil.RunOpts{
Image: "benchmarks/alpine",
- }, "true"); err != nil {
- b.Fatalf("failed to run container: %v", err)
+ }, "sleep", "100"); err != nil {
+ b.Fatalf("failed to start container: %v", err)
}
+ b.StopTimer()
+ container.CleanUp(ctx)
harness.DebugLog(b, "Ran container: %d", i)
}
}
diff --git a/test/benchmarks/tcp/tcp_proxy.go b/test/benchmarks/tcp/tcp_proxy.go
index be74e4d4a..bc0d8fd38 100644
--- a/test/benchmarks/tcp/tcp_proxy.go
+++ b/test/benchmarks/tcp/tcp_proxy.go
@@ -208,8 +208,12 @@ func newNetstackImpl(mode string) (impl, error) {
if err := s.CreateNIC(nicID, fifo.New(ep, runtime.GOMAXPROCS(0), 1000)); err != nil {
return nil, fmt.Errorf("error creating NIC %q: %v", *iface, err)
}
- if err := s.AddAddress(nicID, ipv4.ProtocolNumber, parsedAddr); err != nil {
- return nil, fmt.Errorf("error adding IP address to %q: %v", *iface, err)
+ protocolAddr := tcpip.ProtocolAddress{
+ Protocol: ipv4.ProtocolNumber,
+ AddressWithPrefix: parsedAddr.WithPrefix(),
+ }
+ if err := s.AddProtocolAddress(nicID, protocolAddr, stack.AddressProperties{}); err != nil {
+ return nil, fmt.Errorf("error adding IP address %+v to %q: %w", protocolAddr, *iface, err)
}
subnet, err := tcpip.NewSubnet(parsedDest, parsedMask)
diff --git a/test/benchmarks/tools/fio_test.go b/test/benchmarks/tools/fio_test.go
index a98277150..3b1f852ce 100644
--- a/test/benchmarks/tools/fio_test.go
+++ b/test/benchmarks/tools/fio_test.go
@@ -86,7 +86,7 @@ func TestFio(t *testing.T) {
fio := Fio{}
// WriteBandwidth.
got, err := fio.parseBandwidth(sampleData, false)
- var want float64 = 1753471.0 * 1024
+ want := 1753471.0 * 1024
if err != nil {
t.Fatalf("parse failed with err: %v", err)
} else if got != want {
diff --git a/test/packetimpact/README.md b/test/packetimpact/README.md
index fe0976ba5..3f0f9d155 100644
--- a/test/packetimpact/README.md
+++ b/test/packetimpact/README.md
@@ -16,7 +16,7 @@ Packetimpact aims to provide:
* A **multi-platform** solution that can test both Linux and gVisor.
* **Conciseness** on par with packetdrill scripts.
* **Control-flow** like for loops, conditionals, and variables.
-* **Flexibilty** to specify every byte in a packet or use multiple sockets.
+* **Flexibility** to specify every byte in a packet or use multiple sockets.
## How to run packetimpact tests?
@@ -177,7 +177,7 @@ message SocketResponse {
The test bench does most of the work in a test. It is a Go program that compiles
on the host and is copied by the script into test bench's container. It is a
regular [go unit test](https://golang.org/pkg/testing/) that imports the test
-bench framework. The test bench framwork is based on three basic utilities:
+bench framework. The test bench framework is based on three basic utilities:
* Commanding the DUT to run POSIX commands and return responses.
* Sending raw packets to the DUT on the test network.
@@ -475,9 +475,9 @@ type layerState interface {
// as it was sent is available.
sent(sent Layer) error
- // received updates the layerState based on a Layer that is receieved. The
+ // received updates the layerState based on a Layer that is received. The
// input is a Layer with all prev and next pointers populated so that the
- // entire frame as it was receieved is available.
+ // entire frame as it was received is available.
received(received Layer) error
// close frees associated resources held by the LayerState.
diff --git a/test/packetimpact/dut/posix_server.cc b/test/packetimpact/dut/posix_server.cc
index ea83bbe72..49f41c887 100644
--- a/test/packetimpact/dut/posix_server.cc
+++ b/test/packetimpact/dut/posix_server.cc
@@ -28,10 +28,10 @@
#include <iostream>
#include <unordered_map>
+#include "absl/strings/str_format.h"
#include "include/grpcpp/security/server_credentials.h"
#include "include/grpcpp/server_builder.h"
#include "include/grpcpp/server_context.h"
-#include "absl/strings/str_format.h"
#include "test/packetimpact/proto/posix_server.grpc.pb.h"
#include "test/packetimpact/proto/posix_server.pb.h"
diff --git a/test/packetimpact/runner/dut.go b/test/packetimpact/runner/dut.go
index 02678a76a..f27e52f93 100644
--- a/test/packetimpact/runner/dut.go
+++ b/test/packetimpact/runner/dut.go
@@ -331,6 +331,22 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
t.Logf("sniffer logs:\n%s", snifferOut)
}
})
+ }
+
+ // Arm the cleanup hook before we do anthing else. Otherwise failures below
+ // can cause the test to hang in the cleanup hook above.
+ t.Cleanup(func() {
+ // Wait 1 second before killing tcpdump to give it time to flush
+ // any packets. On linux tests killing it immediately can
+ // sometimes result in partial pcaps.
+ time.Sleep(1 * time.Second)
+ if logs, err := testbenchContainer.Exec(ctx, dockerutil.ExecOpts{}, "killall", baseSnifferArgs[0]); err != nil {
+ t.Errorf("failed to kill all sniffers: %s, logs: %s", err, logs)
+ }
+ })
+
+ for _, info := range dutInfos {
+ n := info.Net
// When the Linux kernel receives a SYN-ACK for a SYN it didn't send, it
// will respond with an RST. In most packetimpact tests, the SYN is sent
// by the raw socket, the kernel knows nothing about the connection, this
@@ -344,16 +360,6 @@ func TestWithDUT(ctx context.Context, t *testing.T, mkDevice func(*dockerutil.Co
}
}
- t.Cleanup(func() {
- // Wait 1 second before killing tcpdump to give it time to flush
- // any packets. On linux tests killing it immediately can
- // sometimes result in partial pcaps.
- time.Sleep(1 * time.Second)
- if logs, err := testbenchContainer.Exec(ctx, dockerutil.ExecOpts{}, "killall", baseSnifferArgs[0]); err != nil {
- t.Errorf("failed to kill all sniffers: %s, logs: %s", err, logs)
- }
- })
-
// FIXME(b/156449515): Some piece of the system has a race. The old
// bash script version had a sleep, so we have one too. The race should
// be fixed and this sleep removed.
diff --git a/test/packetimpact/tests/tcp_listen_backlog_test.go b/test/packetimpact/tests/tcp_listen_backlog_test.go
index fea7d5b6f..e124002f6 100644
--- a/test/packetimpact/tests/tcp_listen_backlog_test.go
+++ b/test/packetimpact/tests/tcp_listen_backlog_test.go
@@ -15,7 +15,9 @@
package tcp_listen_backlog_test
import (
+ "bytes"
"flag"
+ "sync"
"testing"
"time"
@@ -35,60 +37,272 @@ func init() {
func TestTCPListenBacklog(t *testing.T) {
dut := testbench.NewDUT(t)
- // Listening endpoint accepts one more connection than the listen backlog.
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 0 /*backlog*/)
+ // This is the number of pending connections before SYN cookies are used.
+ const backlog = 10
- var establishedConn testbench.TCPIPv4
- var incompleteConn testbench.TCPIPv4
+ listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, backlog)
+ defer dut.Close(t, listenFd)
- // Test if the DUT listener replies to more SYNs than listen backlog+1
- for i, conn := range []*testbench.TCPIPv4{&establishedConn, &incompleteConn} {
- *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- // Expect dut connection to have transitioned to SYN-RCVD state.
+ // Fill the SYN queue with connections in SYN-RCVD. We will use these to test
+ // that ACKs received while the accept queue is full are ignored.
+ var synQueueConns [backlog]testbench.TCPIPv4
+ defer func() {
+ for i := range synQueueConns {
+ synQueueConns[i].Close(t)
+ }
+ }()
+ {
+ var wg sync.WaitGroup
+ for i := range synQueueConns {
+ conn := &synQueueConns[i]
+ *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
+
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ }(i)
+ }
+ wg.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
+ }
+
+ const payloadLen = 1
+ payload := testbench.Payload{Bytes: testbench.GenerateRandomPayload(t, payloadLen)}
+
+ // Fill the accept queue with connections established using SYN cookies.
+ var synCookieConns [backlog + 1]testbench.TCPIPv4
+ defer func() {
+ for i := range synCookieConns {
+ synCookieConns[i].Close(t)
+ }
+ }()
+ {
+ var wg sync.WaitGroup
+ for i := range synCookieConns {
+ conn := &synCookieConns[i]
+ *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
+
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ // Send a payload so we can observe the dut ACK.
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, &payload)
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ }(i)
+ }
+ wg.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
+ }
+
+ // Send ACKs to complete the handshakes. These are expected to be dropped
+ // because the accept queue is full.
+ {
+ var wg sync.WaitGroup
+ for i := range synQueueConns {
+ conn := &synQueueConns[i]
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ // Wait for the SYN-ACK to be retransmitted to confirm the ACK was
+ // dropped.
+ seqNum := uint32(*conn.RemoteSeqNum(t) - 1)
+ if got, err := conn.Expect(t, testbench.TCP{SeqNum: &seqNum}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ }(i)
+ }
+
+ wg.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
+ }
+
+ // While the accept queue is still full, send an unexpected ACK from a new
+ // socket. The listener should reply with an RST.
+ func() {
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
+ defer conn.Close(t)
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("expected TCP frame: %s", err)
+ } else if got, want := *got.Flags, header.TCPFlagRst; got != want {
+ t.Errorf("got %s, want %s", got, want)
+ }
+ }()
+
+ func() {
+ // Now initiate a new connection when the accept queue is full.
+ conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
+ defer conn.Close(t)
+ // Expect dut connection to drop the SYN.
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if _, err := conn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err != nil {
- t.Fatalf("expected SYN-ACK for %d connection, %s", i, err)
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err == nil {
+ t.Fatalf("expected no TCP frame, got %s", got)
+ }
+ }()
+
+ // Drain the accept queue.
+ {
+ var wg sync.WaitGroup
+ for i := range synCookieConns {
+ conn := &synCookieConns[i]
+
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ fd, _ := dut.Accept(t, listenFd)
+ b := dut.Recv(t, fd, payloadLen+1, 0)
+ dut.Close(t, fd)
+ if !bytes.Equal(b, payload.Bytes) {
+ t.Errorf("connection %d: got dut.Recv = %x, want = %x", i, b, payload.Bytes)
+ }
+
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagFin|header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+
+ // Prevent retransmission.
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
+ }(i)
+ }
+ wg.Wait()
+ if t.Failed() {
+ t.FailNow()
}
}
- defer establishedConn.Close(t)
- defer incompleteConn.Close(t)
-
- // Send the ACK to complete handshake.
- establishedConn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
-
- // Poll for the established connection ready for accept.
- dut.PollOne(t, listenFd, unix.POLLIN, time.Second)
-
- // Send the ACK to complete handshake, expect this to be dropped by the
- // listener as the accept queue would be full because of the previous
- // handshake.
- incompleteConn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- // Let the test wait for sometime so that the ACK is indeed dropped by
- // the listener. Without such a wait, the DUT accept can race with
- // ACK handling (dropping) causing the test to be flaky.
- time.Sleep(100 * time.Millisecond)
-
- // Drain the accept queue to enable poll for subsequent connections on the
- // listener.
- fd, _ := dut.Accept(t, listenFd)
- dut.Close(t, fd)
-
- // The ACK for the incomplete connection should be ignored by the
- // listening endpoint and the poll on listener should now time out.
- if pfds := dut.Poll(t, []unix.PollFd{{Fd: listenFd, Events: unix.POLLIN}}, time.Second); len(pfds) != 0 {
- t.Fatalf("got dut.Poll(...) = %#v", pfds)
+
+ // Complete the partial connections to move them from the SYN queue to the
+ // accept queue. We will use these to test that connections in the accept
+ // queue are closed on listener shutdown.
+ {
+ var wg sync.WaitGroup
+ for i := range synQueueConns {
+ conn := &synQueueConns[i]
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ tcp := testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}
+
+ // Exercise connections with and without pending data.
+ if i%2 == 0 {
+ // Send ACK with no payload; wait for absence of SYN-ACK retransmit.
+ conn.Send(t, tcp)
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err == nil {
+ t.Errorf("%d: expected no TCP frame, got %s", i, got)
+ }
+ } else {
+ // Send ACK with payload; wait for ACK.
+ conn.Send(t, tcp, &payload)
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ }
+ }(i)
+ }
+
+ wg.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
}
- // Re-send the ACK to complete handshake and re-fill the accept-queue.
- incompleteConn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
- dut.PollOne(t, listenFd, unix.POLLIN, time.Second)
-
- // Now initiate a new connection when the accept queue is full.
- connectingConn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer connectingConn.Close(t)
- // Expect dut connection to drop the SYN and let the client stay in SYN_SENT state.
- connectingConn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
- if got, err := connectingConn.ExpectData(t, &testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)}, nil, time.Second); err == nil {
- t.Fatalf("expected no SYN-ACK, but got %s", got)
+ // The accept queue now has N-1 connections in it. The next incoming SYN will
+ // enter the SYN queue, and the one following will use SYN cookies. We test
+ // both.
+ var connectingConns [2]testbench.TCPIPv4
+ defer func() {
+ for i := range connectingConns {
+ connectingConns[i].Close(t)
+ }
+ }()
+ {
+ var wg sync.WaitGroup
+ for i := range connectingConns {
+ conn := &connectingConns[i]
+ *conn = dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{})
+
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)})
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagSyn|header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ }(i)
+ }
+ wg.Wait()
+ if t.Failed() {
+ t.FailNow()
+ }
+ }
+
+ dut.Shutdown(t, listenFd, unix.SHUT_RD)
+
+ var wg sync.WaitGroup
+
+ // Shutdown causes Connections in the accept queue to be closed.
+ for i := range synQueueConns {
+ conn := &synQueueConns[i]
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err != nil {
+ t.Errorf("%d: expected TCP frame: %s", i, err)
+ } else if got, want := *got.Flags, header.TCPFlagRst|header.TCPFlagAck; got != want {
+ t.Errorf("%d: got %s, want %s", i, got, want)
+ }
+ }(i)
+ }
+
+ for i := range connectingConns {
+ conn := &connectingConns[i]
+
+ wg.Add(1)
+ go func(i int) {
+ defer wg.Done()
+
+ if got, err := conn.Expect(t, testbench.TCP{}, time.Second); err == nil {
+ t.Errorf("%d: expected no TCP frame, got %s", i, got)
+ }
+ }(i)
}
+
+ wg.Wait()
}
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
index bb33ca4b3..556cee1d9 100644
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
@@ -155,7 +155,7 @@ func testRecv(ctx context.Context, t *testing.T, d testData) {
// testSendTo tests observing the ICMP error through the send syscall. If
// wantErrno is non-zero, the first send should fail and a subsequent send
-// should suceed; while if wantErrno is zero then the first send should just
+// should succeed; while if wantErrno is zero then the first send should just
// succeed.
func testSendTo(ctx context.Context, t *testing.T, d testData) {
// Check that sending on the clean socket works.
diff --git a/test/perf/BUILD b/test/perf/BUILD
index 58fe333ee..59923da47 100644
--- a/test/perf/BUILD
+++ b/test/perf/BUILD
@@ -174,3 +174,10 @@ syscall_test(
test = "//test/perf/linux:verity_open_read_close_benchmark",
vfs1 = False,
)
+
+syscall_test(
+ size = "large",
+ debug = False,
+ test = "//test/perf/linux:verity_stat_benchmark",
+ vfs1 = False,
+)
diff --git a/test/perf/linux/BUILD b/test/perf/linux/BUILD
index 61ed98ff5..020a69ab5 100644
--- a/test/perf/linux/BUILD
+++ b/test/perf/linux/BUILD
@@ -462,3 +462,23 @@ cc_binary(
"//test/util:verity_util",
],
)
+
+cc_binary(
+ name = "verity_stat_benchmark",
+ testonly = 1,
+ srcs = [
+ "verity_stat_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:capability_util",
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "//test/util:verity_util",
+ "@com_google_absl//absl/strings",
+ ],
+)
diff --git a/test/perf/linux/randread_benchmark.cc b/test/perf/linux/randread_benchmark.cc
index b0eb8c24e..11b56a8cb 100644
--- a/test/perf/linux/randread_benchmark.cc
+++ b/test/perf/linux/randread_benchmark.cc
@@ -85,7 +85,7 @@ void BM_RandRead(benchmark::State& state) {
unsigned int seed = 1;
for (auto _ : state) {
TEST_CHECK(PreadFd(fd.get(), buf.data(), buf.size(),
- rand_r(&seed) % kFileSize) == size);
+ rand_r(&seed) % (kFileSize - buf.size())) == size);
}
state.SetBytesProcessed(static_cast<int64_t>(size) *
diff --git a/test/perf/linux/verity_stat_benchmark.cc b/test/perf/linux/verity_stat_benchmark.cc
new file mode 100644
index 000000000..b43a9e266
--- /dev/null
+++ b/test/perf/linux/verity_stat_benchmark.cc
@@ -0,0 +1,84 @@
+// Copyright 2021 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 <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
+#include "benchmark/benchmark.h"
+#include "test/util/fs_util.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/verity_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+// Creates a file in a nested directory hierarchy at least `depth` directories
+// deep, and stats that file multiple times.
+void BM_VerityStat(benchmark::State& state) {
+ // Create nested directories with given depth.
+ int depth = state.range(0);
+
+ // Mount a tmpfs file system to be wrapped by a verity fs.
+ TempPath top_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ TEST_CHECK(mount("", top_dir.path().c_str(), "tmpfs", 0, "") == 0);
+ std::string dir_path = top_dir.path();
+ std::string child_path = "";
+ std::vector<EnableTarget> targets;
+
+ while (depth-- > 0) {
+ // Don't use TempPath because it will make paths too long to use.
+ //
+ // The top_dir destructor will clean up this whole tree.
+ dir_path = JoinPath(dir_path, absl::StrCat(depth));
+ ASSERT_NO_ERRNO(Mkdir(dir_path, 0755));
+ child_path = JoinPath(child_path, Basename(dir_path));
+ targets.emplace_back(EnableTarget(child_path, O_RDONLY));
+ }
+
+ // Create the file that will be stat'd.
+ const TempPath file =
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir_path));
+
+ targets.emplace_back(
+ EnableTarget(JoinPath(child_path, Basename(file.path())), O_RDONLY));
+
+ // Reverse the targets because verity should be enabled from the lowest level.
+ std::reverse(targets.begin(), targets.end());
+
+ std::string verity_dir =
+ TEST_CHECK_NO_ERRNO_AND_VALUE(MountVerity(top_dir.path(), targets));
+
+ struct stat st;
+ for (auto _ : state) {
+ ASSERT_THAT(stat(JoinPath(verity_dir, targets[0].path).c_str(), &st),
+ SyscallSucceeds());
+ }
+}
+
+BENCHMARK(BM_VerityStat)->Range(1, 100)->UseRealTime();
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/runner/gtest/gtest.go b/test/runner/gtest/gtest.go
index 38e57d62f..affbf1973 100644
--- a/test/runner/gtest/gtest.go
+++ b/test/runner/gtest/gtest.go
@@ -133,17 +133,28 @@ func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]Tes
}
// Run again to extract benchmarks.
- args = append([]string{listBenchmarkFlag}, extraArgs...)
- cmd = exec.Command(testBin, args...)
- out, err = cmd.Output()
+ tb, err := ParseBenchmarks(testBin, extraArgs...)
+ if err != nil {
+ return nil, err
+ }
+ t = append(t, tb...)
+ return t, nil
+}
+
+// ParseBenchmarks returns each benchmark in a third_party/benchmark binary's list as a single test case.
+func ParseBenchmarks(binary string, extraArgs ...string) ([]TestCase, error) {
+ var t []TestCase
+ args := append([]string{listBenchmarkFlag}, extraArgs...)
+ cmd := exec.Command(binary, args...)
+ out, err := cmd.Output()
if err != nil {
// We were able to enumerate tests above, but not benchmarks?
// We requested them, so we return an error in this case.
exitErr, ok := err.(*exec.ExitError)
if !ok {
- return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v", err)
+ return nil, fmt.Errorf("could not enumerate benchmarks: %v", err)
}
- return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v\nstderr\n%s", err, exitErr.Stderr)
+ return nil, fmt.Errorf("could not enumerate benchmarks: %v\nstderr\n%s", err, exitErr.Stderr)
}
benches := strings.Trim(string(out), "\n")
diff --git a/test/runner/main.go b/test/runner/main.go
index 34e9c6279..2cab8d2d4 100644
--- a/test/runner/main.go
+++ b/test/runner/main.go
@@ -170,6 +170,7 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
"-network", *network,
"-log-format=text",
"-TESTONLY-unsafe-nonroot=true",
+ "-TESTONLY-allow-packet-endpoint-write=true",
"-net-raw=true",
fmt.Sprintf("-panic-signal=%d", unix.SIGTERM),
"-watchdog-action=panic",
diff --git a/test/runtimes/runner/lib/BUILD b/test/runtimes/runner/lib/BUILD
index d308f41b0..3491c535b 100644
--- a/test/runtimes/runner/lib/BUILD
+++ b/test/runtimes/runner/lib/BUILD
@@ -5,7 +5,11 @@ package(licenses = ["notice"])
go_library(
name = "lib",
testonly = 1,
- srcs = ["lib.go"],
+ srcs = [
+ "go_test_dependency_go118.go",
+ "go_test_dependency_not_go118.go",
+ "lib.go",
+ ],
visibility = ["//test/runtimes/runner:__pkg__"],
deps = [
"//pkg/log",
diff --git a/test/runtimes/runner/lib/go_test_dependency_go118.go b/test/runtimes/runner/lib/go_test_dependency_go118.go
new file mode 100644
index 000000000..d430e81c7
--- /dev/null
+++ b/test/runtimes/runner/lib/go_test_dependency_go118.go
@@ -0,0 +1,27 @@
+// Copyright 2021 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.
+
+//go:build go1.18 && go1.1
+// +build go1.18,go1.1
+
+package lib
+
+import (
+ "testing"
+)
+
+// mainStart wraps testing.MainStart for Go release == 1.18.
+func mainStart(tests []testing.InternalTest) *testing.M {
+ return testing.MainStart(testDeps{}, tests, nil, nil, nil)
+}
diff --git a/test/runtimes/runner/lib/go_test_dependency_not_go118.go b/test/runtimes/runner/lib/go_test_dependency_not_go118.go
new file mode 100644
index 000000000..8b0b34c72
--- /dev/null
+++ b/test/runtimes/runner/lib/go_test_dependency_not_go118.go
@@ -0,0 +1,25 @@
+// Copyright 2021 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.
+
+//go:build !go1.18 && go1.1
+// +build !go1.18,go1.1
+
+package lib
+
+import "testing"
+
+// mainStart wraps testing.MainStart for Go release < 1.18.
+func mainStart(tests []testing.InternalTest) *testing.M {
+ return testing.MainStart(testDeps{}, tests, nil, nil)
+}
diff --git a/test/runtimes/runner/lib/lib.go b/test/runtimes/runner/lib/lib.go
index d6b652897..d704f8895 100644
--- a/test/runtimes/runner/lib/lib.go
+++ b/test/runtimes/runner/lib/lib.go
@@ -21,6 +21,7 @@ import (
"fmt"
"io"
"os"
+ "reflect"
"sort"
"strings"
"testing"
@@ -63,8 +64,7 @@ func RunTests(lang, image, excludeFile string, batchSize int, timeout time.Durat
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
return 1
}
-
- m := testing.MainStart(testDeps{}, tests, nil, nil)
+ m := mainStart(tests)
return m.Run()
}
@@ -197,3 +197,21 @@ func (f testDeps) ImportPath() string { return "" }
func (f testDeps) StartTestLog(io.Writer) {}
func (f testDeps) StopTestLog() error { return nil }
func (f testDeps) SetPanicOnExit0(bool) {}
+func (f testDeps) CoordinateFuzzing(time.Duration, int64, time.Duration, int64, int, []corpusEntry, []reflect.Type, string, string) error {
+ return nil
+}
+func (f testDeps) RunFuzzWorker(func(corpusEntry) error) error { return nil }
+func (f testDeps) ReadCorpus(string, []reflect.Type) ([]corpusEntry, error) { return nil, nil }
+func (f testDeps) CheckCorpus([]interface{}, []reflect.Type) error { return nil }
+func (f testDeps) ResetCoverage() {}
+func (f testDeps) SnapshotCoverage() {}
+
+// Copied from testing/fuzz.go.
+type corpusEntry = struct {
+ Parent string
+ Name string
+ Data []byte
+ Values []interface{}
+ Generation int
+ IsSeed bool
+}
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index 16c451786..168766dd8 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -359,6 +359,10 @@ syscall_test(
)
syscall_test(
+ test = "//test/syscalls/linux:packet_socket_dgram_test",
+)
+
+syscall_test(
test = "//test/syscalls/linux:packet_socket_raw_test",
)
@@ -430,6 +434,11 @@ syscall_test(
)
syscall_test(
+ tags = ["container"],
+ test = "//test/syscalls/linux:proc_isolated_test",
+)
+
+syscall_test(
test = "//test/syscalls/linux:proc_net_test",
)
@@ -707,6 +716,11 @@ syscall_test(
syscall_test(
size = "medium",
+ test = "//test/syscalls/linux:socket_ipv4_datagram_based_socket_unbound_loopback_test",
+)
+
+syscall_test(
+ size = "medium",
test = "//test/syscalls/linux:socket_ipv4_udp_unbound_loopback_test",
)
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index 9c7bb8394..45f6304e5 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -7,6 +7,12 @@ package(
exports_files(
[
+ "packet_socket.cc",
+ "packet_socket_dgram.cc",
+ "packet_socket_raw.cc",
+ "raw_socket.cc",
+ "raw_socket_hdrincl.cc",
+ "raw_socket_icmp.cc",
"socket.cc",
"socket_inet_loopback.cc",
"socket_inet_loopback_isolated.cc",
@@ -21,6 +27,7 @@ exports_files(
"socket_ip_udp_loopback_blocking.cc",
"socket_ip_udp_loopback_nonblock.cc",
"socket_ip_unbound.cc",
+ "socket_ipv4_datagram_based_socket_unbound_loopback.cc",
"socket_ipv4_udp_unbound_external_networking_test.cc",
"socket_ipv6_udp_unbound_external_networking_test.cc",
"socket_ipv4_udp_unbound_loopback.cc",
@@ -581,6 +588,7 @@ cc_binary(
"//test/util:file_descriptor",
gtest,
"//test/util:posix_error",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
"//test/util:thread_util",
@@ -1436,14 +1444,15 @@ cc_binary(
)
cc_binary(
- name = "packet_socket_raw_test",
+ name = "packet_socket_dgram_test",
testonly = 1,
- srcs = ["packet_socket_raw.cc"],
- defines = select_system(),
+ srcs = ["packet_socket_dgram.cc"],
linkstatic = 1,
deps = [
+ ":ip_socket_test_util",
":unix_domain_socket_test_util",
"//test/util:capability_util",
+ "//test/util:cleanup",
"//test/util:file_descriptor",
"//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
@@ -1455,13 +1464,15 @@ cc_binary(
)
cc_binary(
- name = "packet_socket_test",
+ name = "packet_socket_raw_test",
testonly = 1,
- srcs = ["packet_socket.cc"],
+ srcs = ["packet_socket_raw.cc"],
linkstatic = 1,
deps = [
+ ":ip_socket_test_util",
":unix_domain_socket_test_util",
"//test/util:capability_util",
+ "//test/util:cleanup",
"//test/util:file_descriptor",
"//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
@@ -1473,6 +1484,21 @@ cc_binary(
)
cc_binary(
+ name = "packet_socket_test",
+ testonly = 1,
+ srcs = ["packet_socket.cc"],
+ linkstatic = 1,
+ deps = [
+ ":ip_socket_test_util",
+ "//test/util:capability_util",
+ "//test/util:file_descriptor",
+ "//test/util:socket_util",
+ gtest,
+ "//test/util:test_main",
+ ],
+)
+
+cc_binary(
name = "pty_test",
testonly = 1,
srcs = ["pty.cc"],
@@ -1769,6 +1795,20 @@ cc_binary(
)
cc_binary(
+ name = "proc_isolated_test",
+ testonly = 1,
+ srcs = ["proc_isolated.cc"],
+ linkstatic = 1,
+ deps = [
+ "@com_google_absl//absl/strings",
+ gtest,
+ "//test/util:fs_util",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ ],
+)
+
+cc_binary(
name = "proc_net_test",
testonly = 1,
srcs = ["proc_net.cc"],
@@ -2526,6 +2566,24 @@ cc_library(
)
cc_library(
+ name = "socket_ipv4_datagram_based_socket_unbound_test_cases",
+ testonly = 1,
+ srcs = [
+ "socket_ipv4_datagram_based_socket_unbound.cc",
+ ],
+ hdrs = [
+ "socket_ipv4_datagram_based_socket_unbound.h",
+ ],
+ deps = [
+ ":ip_socket_test_util",
+ "//test/util:capability_util",
+ "//test/util:socket_util",
+ gtest,
+ ],
+ alwayslink = 1,
+)
+
+cc_library(
name = "socket_ipv4_udp_unbound_test_cases",
testonly = 1,
srcs = [
@@ -2539,9 +2597,6 @@ cc_library(
"//test/util:socket_util",
"@com_google_absl//absl/memory",
gtest,
- "//test/util:posix_error",
- "//test/util:save_util",
- "//test/util:test_util",
],
alwayslink = 1,
)
@@ -2607,16 +2662,11 @@ cc_library(
cc_library(
name = "socket_ip_udp_unbound_external_networking",
testonly = 1,
- srcs = [
- "socket_ip_udp_unbound_external_networking.cc",
- ],
hdrs = [
"socket_ip_udp_unbound_external_networking.h",
],
deps = [
":ip_socket_test_util",
- "//test/util:socket_util",
- "//test/util:test_util",
],
alwayslink = 1,
)
@@ -2632,6 +2682,9 @@ cc_library(
],
deps = [
":socket_ip_udp_unbound_external_networking",
+ "//test/util:socket_util",
+ "//test/util:test_util",
+ "@com_google_absl//absl/cleanup",
gtest,
],
alwayslink = 1,
@@ -2948,9 +3001,21 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ipv4_udp_unbound_test_cases",
- "//test/util:socket_util",
"//test/util:test_main",
- "//test/util:test_util",
+ ],
+)
+
+cc_binary(
+ name = "socket_ipv4_datagram_based_socket_unbound_loopback_test",
+ testonly = 1,
+ srcs = [
+ "socket_ipv4_datagram_based_socket_unbound_loopback.cc",
+ ],
+ linkstatic = 1,
+ deps = [
+ ":ip_socket_test_util",
+ ":socket_ipv4_datagram_based_socket_unbound_test_cases",
+ "//test/util:test_main",
],
)
@@ -3287,9 +3352,12 @@ cc_library(
],
deps = [
":unix_domain_socket_test_util",
+ "//test/util:file_descriptor",
+ "//test/util:memory_util",
"//test/util:socket_util",
"@com_google_absl//absl/strings",
gtest,
+ "//test/util:temp_path",
"//test/util:test_util",
"//test/util:thread_util",
],
@@ -4123,12 +4191,10 @@ cc_binary(
srcs = ["network_namespace.cc"],
linkstatic = 1,
deps = [
- "//test/util:socket_util",
gtest,
+ ":ip_socket_test_util",
"//test/util:capability_util",
- "//test/util:posix_error",
"//test/util:test_main",
- "//test/util:test_util",
"//test/util:thread_util",
],
)
diff --git a/test/syscalls/linux/cgroup.cc b/test/syscalls/linux/cgroup.cc
index f29891571..ca23dfeee 100644
--- a/test/syscalls/linux/cgroup.cc
+++ b/test/syscalls/linux/cgroup.cc
@@ -279,6 +279,23 @@ TEST(Cgroup, UnmountRepeated) {
EXPECT_THAT(umount(c.Path().c_str()), SyscallFailsWithErrno(EINVAL));
}
+TEST(Cgroup, Create) {
+ SKIP_IF(!CgroupsAvailable());
+ Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()));
+ Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs(""));
+ ASSERT_NO_ERRNO(c.CreateChild("child1"));
+ EXPECT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(Exists(c.Path())));
+}
+
+TEST(Cgroup, SubcontainerInitiallyEmpty) {
+ SKIP_IF(!CgroupsAvailable());
+ Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()));
+ Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs(""));
+ Cgroup child = ASSERT_NO_ERRNO_AND_VALUE(c.CreateChild("child1"));
+ auto procs = ASSERT_NO_ERRNO_AND_VALUE(child.Procs());
+ EXPECT_TRUE(procs.empty());
+}
+
TEST(MemoryCgroup, MemoryUsageInBytes) {
SKIP_IF(!CgroupsAvailable());
diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc
index 89b16d168..600a40ef8 100644
--- a/test/syscalls/linux/epoll.cc
+++ b/test/syscalls/linux/epoll.cc
@@ -30,6 +30,7 @@
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
#include "test/util/test_util.h"
+#include "test/util/thread_util.h"
namespace gvisor {
namespace testing {
@@ -497,47 +498,39 @@ TEST(EpollTest, PipeReaderHupAfterWriterClosed) {
}
TEST(EpollTest, DoubleLayerEpoll) {
- int listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- struct sockaddr_in sa;
- sa.sin_family = AF_INET;
- inet_aton("127.0.0.1", &sa.sin_addr);
- sa.sin_port = htons(8888);
- bind(listen_fd, (struct sockaddr *)&sa, sizeof(sa));
- listen(listen_fd, 1);
+ DisableSave ds;
+ int pipefds[2];
+ ASSERT_THAT(pipe(pipefds), SyscallSucceeds());
+ FileDescriptor rfd(pipefds[0]);
+ FileDescriptor wfd(pipefds[1]);
auto epfd1 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- ASSERT_NO_ERRNO(RegisterEpollFD(epfd1.get(), listen_fd, EPOLLIN|EPOLLHUP, listen_fd));
+ ASSERT_NO_ERRNO(
+ RegisterEpollFD(epfd1.get(), rfd.get(), EPOLLIN | EPOLLHUP, rfd.get()));
auto epfd2 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD());
- ASSERT_NO_ERRNO(RegisterEpollFD(epfd2.get(), epfd1.get(), EPOLLIN|EPOLLHUP, epfd1.get()));
-
- struct epoll_event ret_events[2];
-
- // first time
- ScopedThread thread1([&sa]() {
- sleep(1);
- int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- ASSERT_THAT(connect(sock, (struct sockaddr *)&sa, sizeof(sa)), SyscallSucceeds());
- });
- ASSERT_THAT(epoll_wait(epfd2.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
- ASSERT_EQ(ret_events[0].data.fd, epfd1.get());
- ASSERT_THAT(epoll_wait(epfd1.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
- ASSERT_EQ(ret_events[0].data.fd, listen_fd);
- int server_fd = accept(listen_fd, NULL, NULL);
- ASSERT_THAT(close(server_fd), SyscallSucceeds());
-
- // second time to check if event in epfd1 is cleaned correctly
- ScopedThread thread2([&sa]() {
- sleep(1);
- int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- ASSERT_THAT(connect(sock, (struct sockaddr *)&sa, sizeof(sa)), SyscallSucceeds());
- });
- ASSERT_THAT(epoll_wait(epfd2.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
- ASSERT_EQ(ret_events[0].data.fd, epfd1.get());
- ASSERT_THAT(epoll_wait(epfd1.get(), ret_events, 2, 2000), SyscallSucceedsWithValue(1));
- ASSERT_EQ(ret_events[0].data.fd, listen_fd);
- server_fd = accept(listen_fd, NULL, NULL);
- ASSERT_THAT(close(server_fd), SyscallSucceeds());
+ ASSERT_NO_ERRNO(RegisterEpollFD(epfd2.get(), epfd1.get(), EPOLLIN | EPOLLHUP,
+ epfd1.get()));
+
+ // Write to wfd and then check if epoll events were generated correctly.
+ // Run this loop a couple of times to check if event in epfd1 is cleaned.
+ constexpr char data[] = "data";
+ for (int i = 0; i < 2; ++i) {
+ ScopedThread thread1([&wfd, &data]() {
+ sleep(1);
+ ASSERT_EQ(WriteFd(wfd.get(), data, sizeof(data)), sizeof(data));
+ });
+
+ struct epoll_event ret_events[2];
+ ASSERT_THAT(RetryEINTR(epoll_wait)(epfd2.get(), ret_events, 2, 2000),
+ SyscallSucceedsWithValue(1));
+ ASSERT_EQ(ret_events[0].data.fd, epfd1.get());
+ ASSERT_THAT(RetryEINTR(epoll_wait)(epfd1.get(), ret_events, 2, 2000),
+ SyscallSucceedsWithValue(1));
+ ASSERT_EQ(ret_events[0].data.fd, rfd.get());
+ char readBuf[sizeof(data)];
+ ASSERT_EQ(ReadFd(rfd.get(), readBuf, sizeof(data)), sizeof(data));
+ }
}
} // namespace
diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc
index 8202d35fa..a2cc59e83 100644
--- a/test/syscalls/linux/eventfd.cc
+++ b/test/syscalls/linux/eventfd.cc
@@ -149,31 +149,6 @@ TEST(EventfdTest, BigWriteBigRead) {
EXPECT_EQ(l[0], 1);
}
-TEST(EventfdTest, SpliceFromPipePartialSucceeds) {
- int pipes[2];
- ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds());
- const FileDescriptor pipe_rfd(pipes[0]);
- const FileDescriptor pipe_wfd(pipes[1]);
- constexpr uint64_t kVal{1};
-
- FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK));
-
- uint64_t event_array[2];
- event_array[0] = kVal;
- event_array[1] = kVal;
- ASSERT_THAT(write(pipe_wfd.get(), event_array, sizeof(event_array)),
- SyscallSucceedsWithValue(sizeof(event_array)));
- EXPECT_THAT(splice(pipe_rfd.get(), /*__offin=*/nullptr, efd.get(),
- /*__offout=*/nullptr, sizeof(event_array[0]) + 1,
- SPLICE_F_NONBLOCK),
- SyscallSucceedsWithValue(sizeof(event_array[0])));
-
- uint64_t val;
- ASSERT_THAT(read(efd.get(), &val, sizeof(val)),
- SyscallSucceedsWithValue(sizeof(val)));
- EXPECT_EQ(val, kVal);
-}
-
// NotifyNonZero is inherently racy, so random save is disabled.
TEST(EventfdTest, NotifyNonZero) {
// Waits will time out at 10 seconds.
diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc
index f6b78989b..e2622232d 100644
--- a/test/syscalls/linux/inotify.cc
+++ b/test/syscalls/linux/inotify.cc
@@ -1849,34 +1849,6 @@ TEST(Inotify, SpliceOnWatchTarget) {
}));
}
-TEST(Inotify, SpliceOnInotifyFD) {
- int pipefds[2];
- ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds());
-
- const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
- const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- root.path(), "some content", TempPath::kDefaultFileMode));
-
- const FileDescriptor file1_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY));
- const int watcher = ASSERT_NO_ERRNO_AND_VALUE(
- InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS));
-
- char buf;
- EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds());
-
- EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr,
- sizeof(struct inotify_event) + 1, SPLICE_F_NONBLOCK),
- SyscallSucceedsWithValue(sizeof(struct inotify_event)));
-
- const FileDescriptor read_fd(pipefds[0]);
- const std::vector<Event> events =
- ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(read_fd.get()));
- ASSERT_THAT(events, Are({Event(IN_ACCESS, watcher)}));
-}
-
// Watches on a parent should not be triggered by actions on a hard link to one
// of its children that has a different parent.
TEST(Inotify, LinkOnOtherParent) {
diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc
index e90a7e411..d18e616d0 100644
--- a/test/syscalls/linux/ip_socket_test_util.cc
+++ b/test/syscalls/linux/ip_socket_test_util.cc
@@ -16,6 +16,7 @@
#include <net/if.h>
#include <netinet/in.h>
+#include <netpacket/packet.h>
#include <sys/socket.h>
#include <cstring>
@@ -156,6 +157,14 @@ SocketKind ICMPv6UnboundSocket(int type) {
UnboundSocketCreator(AF_INET6, type | SOCK_DGRAM, IPPROTO_ICMPV6)};
}
+SocketKind IPv4RawUDPUnboundSocket(int type) {
+ std::string description =
+ absl::StrCat(DescribeSocketType(type), "IPv4 Raw UDP socket");
+ return SocketKind{
+ description, AF_INET, type | SOCK_RAW, IPPROTO_UDP,
+ UnboundSocketCreator(AF_INET, type | SOCK_RAW, IPPROTO_UDP)};
+}
+
SocketKind IPv4UDPUnboundSocket(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket");
@@ -188,75 +197,53 @@ SocketKind IPv6TCPUnboundSocket(int type) {
UnboundSocketCreator(AF_INET6, type | SOCK_STREAM, IPPROTO_TCP)};
}
-PosixError IfAddrHelper::Load() {
- Release();
-#ifndef ANDROID
- RETURN_ERROR_IF_SYSCALL_FAIL(getifaddrs(&ifaddr_));
-#else
- // Android does not support getifaddrs in r22.
- return PosixError(ENOSYS, "getifaddrs");
-#endif
- return NoError();
-}
-
-void IfAddrHelper::Release() {
- if (ifaddr_) {
-#ifndef ANDROID
- // Android does not support freeifaddrs in r22.
- freeifaddrs(ifaddr_);
-#endif
- ifaddr_ = nullptr;
- }
-}
-
-std::vector<std::string> IfAddrHelper::InterfaceList(int family) const {
- std::vector<std::string> names;
- for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) {
- continue;
- }
- names.emplace(names.end(), ifa->ifa_name);
- }
- return names;
-}
-
-const sockaddr* IfAddrHelper::GetAddr(int family, std::string name) const {
- for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) {
- if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) {
- continue;
- }
- if (name == ifa->ifa_name) {
- return ifa->ifa_addr;
- }
- }
- return nullptr;
-}
-
-PosixErrorOr<int> IfAddrHelper::GetIndex(std::string name) const {
- return InterfaceIndex(name);
-}
-
std::string GetAddr4Str(const in_addr* a) {
char str[INET_ADDRSTRLEN];
- inet_ntop(AF_INET, a, str, sizeof(str));
- return std::string(str);
+ return inet_ntop(AF_INET, a, str, sizeof(str));
}
std::string GetAddr6Str(const in6_addr* a) {
char str[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET6, a, str, sizeof(str));
- return std::string(str);
+ return inet_ntop(AF_INET6, a, str, sizeof(str));
}
std::string GetAddrStr(const sockaddr* a) {
- if (a->sa_family == AF_INET) {
- auto src = &(reinterpret_cast<const sockaddr_in*>(a)->sin_addr);
- return GetAddr4Str(src);
- } else if (a->sa_family == AF_INET6) {
- auto src = &(reinterpret_cast<const sockaddr_in6*>(a)->sin6_addr);
- return GetAddr6Str(src);
+ switch (a->sa_family) {
+ case AF_INET: {
+ return GetAddr4Str(&(reinterpret_cast<const sockaddr_in*>(a)->sin_addr));
+ }
+ case AF_INET6: {
+ return GetAddr6Str(
+ &(reinterpret_cast<const sockaddr_in6*>(a)->sin6_addr));
+ }
+ case AF_PACKET: {
+ const sockaddr_ll& ll = *reinterpret_cast<const sockaddr_ll*>(a);
+ std::ostringstream ss;
+ ss << std::hex;
+ ss << std::showbase;
+ ss << '{';
+ ss << " protocol=" << ntohs(ll.sll_protocol);
+ ss << " ifindex=" << ll.sll_ifindex;
+ ss << " hatype=" << ll.sll_hatype;
+ ss << " pkttype=" << static_cast<unsigned short>(ll.sll_pkttype);
+ if (ll.sll_halen != 0) {
+ ss << " addr=";
+ for (unsigned char i = 0; i < ll.sll_halen; ++i) {
+ if (i != 0) {
+ ss << ':';
+ }
+ ss << static_cast<unsigned short>(ll.sll_addr[i]);
+ }
+ }
+ ss << " }";
+ return ss.str();
+ }
+ default: {
+ std::ostringstream ss;
+ ss << "invalid(sa_family=" << a->sa_family << ")";
+ return ss.str();
+ }
}
- return std::string("<invalid>");
}
} // namespace testing
diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h
index 8f26f1cd0..957006e25 100644
--- a/test/syscalls/linux/ip_socket_test_util.h
+++ b/test/syscalls/linux/ip_socket_test_util.h
@@ -35,6 +35,9 @@ uint16_t PortFromInetSockaddr(const struct sockaddr* addr);
// InterfaceIndex returns the index of the named interface.
PosixErrorOr<int> InterfaceIndex(std::string name);
+// GetLoopbackIndex returns the index of the loopback interface.
+inline PosixErrorOr<int> GetLoopbackIndex() { return InterfaceIndex("lo"); }
+
// IPv6TCPAcceptBindSocketPair returns a SocketPairKind that represents
// SocketPairs created with bind() and accept() syscalls with AF_INET6 and the
// given type bound to the IPv6 loopback.
@@ -92,6 +95,10 @@ SocketKind ICMPUnboundSocket(int type);
// created with AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6, and the given type.
SocketKind ICMPv6UnboundSocket(int type);
+// IPv4RawUDPUnboundSocket returns a SocketKind that represents a SimpleSocket
+// created with AF_INET, SOCK_RAW, IPPROTO_UDP, and the given type.
+SocketKind IPv4RawUDPUnboundSocket(int type);
+
// IPv4UDPUnboundSocket returns a SocketKind that represents a SimpleSocket
// created with AF_INET, SOCK_DGRAM, IPPROTO_UDP, and the given type.
SocketKind IPv4UDPUnboundSocket(int type);
@@ -108,25 +115,6 @@ SocketKind IPv4TCPUnboundSocket(int type);
// created with AF_INET6, SOCK_STREAM, IPPROTO_TCP and the given type.
SocketKind IPv6TCPUnboundSocket(int type);
-// IfAddrHelper is a helper class that determines the local interfaces present
-// and provides functions to obtain their names, index numbers, and IP address.
-class IfAddrHelper {
- public:
- IfAddrHelper() : ifaddr_(nullptr) {}
- ~IfAddrHelper() { Release(); }
-
- PosixError Load();
- void Release();
-
- std::vector<std::string> InterfaceList(int family) const;
-
- const sockaddr* GetAddr(int family, std::string name) const;
- PosixErrorOr<int> GetIndex(std::string name) const;
-
- private:
- struct ifaddrs* ifaddr_;
-};
-
// GetAddr4Str returns the given IPv4 network address structure as a string.
std::string GetAddr4Str(const in_addr* a);
diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc
index 4f9ca1a65..8b208f99a 100644
--- a/test/syscalls/linux/link.cc
+++ b/test/syscalls/linux/link.cc
@@ -142,7 +142,8 @@ TEST(LinkTest, OldnameIsEmpty) {
TEST(LinkTest, OldnameDoesNotExist) {
const std::string oldname = NewTempAbsPath();
const std::string newname = NewTempAbsPath();
- EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT));
+ EXPECT_THAT(link(oldname.c_str(), newname.c_str()),
+ SyscallFailsWithErrno(ENOENT));
}
TEST(LinkTest, NewnameCannotExist) {
diff --git a/test/syscalls/linux/mmap.cc b/test/syscalls/linux/mmap.cc
index caad575cf..bb2a0cb57 100644
--- a/test/syscalls/linux/mmap.cc
+++ b/test/syscalls/linux/mmap.cc
@@ -29,6 +29,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <limits>
#include <vector>
#include "gmock/gmock.h"
@@ -795,7 +796,7 @@ class MMapFileTest : public MMapTest {
bool FSSupportsMap() const {
bool supported = true;
- void* ret = mmap(nullptr, 1, PROT_NONE, 0, fd_.get(), 0);
+ void* ret = mmap(nullptr, 1, PROT_NONE, MAP_PRIVATE, fd_.get(), 0);
if (ret == MAP_FAILED && errno != ENODEV) {
supported = false;
}
@@ -913,13 +914,41 @@ TEST_F(MMapFileTest, MapOffsetBeyondEnd) {
::testing::KilledBySignal(SIGBUS), "");
}
-// Verify mmap fails when sum of length and offset overflows.
-TEST_F(MMapFileTest, MapLengthPlusOffsetOverflows) {
+TEST_F(MMapFileTest, MapSecondToLastPositivePage) {
SKIP_IF(!FSSupportsMap());
- const size_t length = static_cast<size_t>(-kPageSize);
- const off_t offset = kPageSize;
- ASSERT_THAT(Map(0, length, PROT_READ, MAP_PRIVATE, fd_.get(), offset),
- SyscallFailsWithErrno(ENOMEM));
+ EXPECT_THAT(
+ Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
+ (std::numeric_limits<off_t>::max() - kPageSize) & ~(kPageSize - 1)),
+ SyscallSucceeds());
+}
+
+TEST_F(MMapFileTest, MapLastPositivePage) {
+ SKIP_IF(!FSSupportsMap());
+ // For regular files, this should fail due to integer overflow of the end
+ // offset.
+ EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
+ std::numeric_limits<off_t>::max() & ~(kPageSize - 1)),
+ SyscallFailsWithErrno(EOVERFLOW));
+}
+
+TEST_F(MMapFileTest, MapFirstNegativePage) {
+ SKIP_IF(!FSSupportsMap());
+ EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
+ std::numeric_limits<off_t>::min()),
+ SyscallFailsWithErrno(EOVERFLOW));
+}
+
+TEST_F(MMapFileTest, MapSecondToLastNegativePage) {
+ SKIP_IF(!FSSupportsMap());
+ EXPECT_THAT(
+ Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), -(2 * kPageSize)),
+ SyscallFailsWithErrno(EOVERFLOW));
+}
+
+TEST_F(MMapFileTest, MapLastNegativePage) {
+ SKIP_IF(!FSSupportsMap());
+ EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), -kPageSize),
+ SyscallFailsWithErrno(EOVERFLOW));
}
// MAP_PRIVATE PROT_WRITE is allowed on read-only FDs.
diff --git a/test/syscalls/linux/msgqueue.cc b/test/syscalls/linux/msgqueue.cc
index 8994b739a..c4761eba8 100644
--- a/test/syscalls/linux/msgqueue.cc
+++ b/test/syscalls/linux/msgqueue.cc
@@ -30,9 +30,15 @@ namespace gvisor {
namespace testing {
namespace {
-constexpr int msgMax = 8192; // Max size for message in bytes.
+// Source: include/uapi/linux/msg.h
+constexpr int msgMnb = 16384; // Maximum number of bytes in a queue.
constexpr int msgMni = 32000; // Max number of identifiers.
-constexpr int msgMnb = 16384; // Default max size of message queue in bytes.
+constexpr int msgPool =
+ (msgMni * msgMnb / 1024); // Size of buffer pool used to hold message data.
+constexpr int msgMap = msgMnb; // Maximum number of entries in message map.
+constexpr int msgMax = 8192; // Maximum number of bytes in a single message.
+constexpr int msgSsz = 16; // Message segment size.
+constexpr int msgTql = msgMnb; // Maximum number of messages on all queues.
constexpr int kInterruptSignal = SIGALRM;
@@ -40,6 +46,10 @@ constexpr int kInterruptSignal = SIGALRM;
class Queue {
public:
explicit Queue(int id) : id_(id) {}
+ Queue(const Queue&) = delete;
+ Queue& operator=(const Queue&) = delete;
+
+ Queue(Queue&& other) { id_ = other.release(); }
~Queue() {
if (id_ >= 0) {
@@ -59,6 +69,14 @@ class Queue {
int id_ = -1;
};
+PosixErrorOr<Queue> Msgget(key_t key, int flags) {
+ int id = msgget(key, flags);
+ if (id == -1) {
+ return PosixError(errno, absl::StrFormat("msgget(%d, %d)", key, flags));
+ }
+ return Queue(id);
+}
+
// Default size for messages.
constexpr size_t msgSize = 50;
@@ -90,8 +108,7 @@ TEST(MsgqueueTest, MsgGet) {
const key_t key = ftok(keyfile.path().c_str(), 1);
ASSERT_THAT(key, SyscallSucceeds());
- Queue queue(msgget(key, IPC_CREAT));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(key, IPC_CREAT));
EXPECT_THAT(msgget(key, 0), SyscallSucceedsWithValue(queue.get()));
}
@@ -103,27 +120,20 @@ TEST(MsgqueueTest, MsgGetFail) {
EXPECT_THAT(msgget(key, 0), SyscallFailsWithErrno(ENOENT));
- Queue queue(msgget(key, IPC_CREAT));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
-
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(key, IPC_CREAT));
EXPECT_THAT(msgget(key, IPC_CREAT | IPC_EXCL), SyscallFailsWithErrno(EEXIST));
}
// Test using msgget(2) with IPC_PRIVATE option.
TEST(MsgqueueTest, MsgGetIpcPrivate) {
- Queue queue1(msgget(IPC_PRIVATE, 0));
- ASSERT_THAT(queue1.get(), SyscallSucceeds());
-
- Queue queue2(msgget(IPC_PRIVATE, 0));
- ASSERT_THAT(queue2.get(), SyscallSucceeds());
-
+ Queue queue1 = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0));
+ Queue queue2 = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0));
EXPECT_NE(queue1.get(), queue2.get());
}
// Test simple msgsnd and msgrcv.
TEST(MsgqueueTest, MsgOpSimple) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, "A message."};
msgbuf rcv;
@@ -138,8 +148,7 @@ TEST(MsgqueueTest, MsgOpSimple) {
// Test msgsnd and msgrcv of an empty message.
TEST(MsgqueueTest, MsgOpEmpty) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, ""};
msgbuf rcv;
@@ -151,8 +160,7 @@ TEST(MsgqueueTest, MsgOpEmpty) {
// Test truncation of message with MSG_NOERROR flag.
TEST(MsgqueueTest, MsgOpTruncate) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, ""};
msgbuf rcv;
@@ -166,8 +174,7 @@ TEST(MsgqueueTest, MsgOpTruncate) {
// Test msgsnd and msgrcv using invalid arguments.
TEST(MsgqueueTest, MsgOpInvalidArgs) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, ""};
@@ -184,8 +191,7 @@ TEST(MsgqueueTest, MsgOpInvalidArgs) {
// Test non-blocking msgrcv with an empty queue.
TEST(MsgqueueTest, MsgOpNoMsg) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf rcv;
EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(rcv.mtext) + 1, 0, IPC_NOWAIT),
@@ -195,8 +201,7 @@ TEST(MsgqueueTest, MsgOpNoMsg) {
// Test non-blocking msgrcv with a non-empty queue, but no messages of wanted
// type.
TEST(MsgqueueTest, MsgOpNoMsgType) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, ""};
ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
@@ -208,8 +213,7 @@ TEST(MsgqueueTest, MsgOpNoMsgType) {
// Test msgrcv with a larger size message than wanted, and truncation disabled.
TEST(MsgqueueTest, MsgOpTooBig) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, ""};
ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
@@ -221,8 +225,7 @@ TEST(MsgqueueTest, MsgOpTooBig) {
// Test receiving messages based on type.
TEST(MsgqueueTest, MsgRcvType) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
// Send messages in an order and receive them in reverse, based on type,
// which shouldn't block.
@@ -248,8 +251,7 @@ TEST(MsgqueueTest, MsgRcvType) {
// Test using MSG_EXCEPT to receive a different-type message.
TEST(MsgqueueTest, MsgExcept) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
std::map<int64_t, msgbuf> typeToBuf = {
{1, msgbuf{1, "Message 1."}},
@@ -274,8 +276,7 @@ TEST(MsgqueueTest, MsgExcept) {
// Test msgrcv with a negative type.
TEST(MsgqueueTest, MsgRcvTypeNegative) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
// When msgtyp is negative, msgrcv returns the first message with mtype less
// than or equal to the absolute value.
@@ -298,8 +299,7 @@ TEST(MsgqueueTest, MsgRcvTypeNegative) {
TEST(MsgqueueTest, MsgOpPermissions) {
AutoCapability cap(CAP_IPC_OWNER, false);
- Queue queue(msgget(IPC_PRIVATE, 0000));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0000));
msgbuf buf{1, ""};
@@ -311,8 +311,7 @@ TEST(MsgqueueTest, MsgOpPermissions) {
// Test limits for messages and queues.
TEST(MsgqueueTest, MsgOpLimits) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, "A message."};
@@ -340,11 +339,14 @@ bool MsgCopySupported() {
// test if errno == ENOSYS. This means that the test will always run on
// gVisor, but may be skipped on native linux.
- Queue queue(msgget(IPC_PRIVATE, 0600));
-
+ auto maybe_id = Msgget(IPC_PRIVATE, 0600);
+ if (!maybe_id.ok()) {
+ return false;
+ }
+ Queue queue(std::move(maybe_id.ValueOrDie()));
msgbuf buf{1, "Test message."};
- msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0);
+ msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0);
return !(msgrcv(queue.get(), &buf, sizeof(buf.mtext) + 1, 0,
MSG_COPY | IPC_NOWAIT) == -1 &&
errno == ENOSYS);
@@ -354,9 +356,7 @@ bool MsgCopySupported() {
TEST(MsgqueueTest, MsgCopy) {
SKIP_IF(!MsgCopySupported());
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
-
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf bufs[5] = {
msgbuf{1, "Message 1."}, msgbuf{2, "Message 2."}, msgbuf{3, "Message 3."},
msgbuf{4, "Message 4."}, msgbuf{5, "Message 5."},
@@ -390,9 +390,7 @@ TEST(MsgqueueTest, MsgCopy) {
TEST(MsgqueueTest, MsgCopyInvalidArgs) {
SKIP_IF(!MsgCopySupported());
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
-
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf rcv;
EXPECT_THAT(msgrcv(queue.get(), &rcv, msgSize, 1, MSG_COPY),
SyscallFailsWithErrno(EINVAL));
@@ -406,9 +404,7 @@ TEST(MsgqueueTest, MsgCopyInvalidArgs) {
TEST(MsgqueueTest, MsgCopyInvalidIndex) {
SKIP_IF(!MsgCopySupported());
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
-
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf rcv;
EXPECT_THAT(msgrcv(queue.get(), &rcv, msgSize, -3, MSG_COPY | IPC_NOWAIT),
SyscallFailsWithErrno(ENOMSG));
@@ -419,9 +415,7 @@ TEST(MsgqueueTest, MsgCopyInvalidIndex) {
// Test msgrcv (most probably) blocking on an empty queue.
TEST(MsgqueueTest, MsgRcvBlocking) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
-
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf buf{1, "A message."};
ScopedThread t([&] {
@@ -441,9 +435,7 @@ TEST(MsgqueueTest, MsgRcvBlocking) {
// Test msgrcv (most probably) waiting for a specific-type message.
TEST(MsgqueueTest, MsgRcvTypeBlocking) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
-
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgbuf bufs[5] = {{1, "A message."},
{1, "A message."},
{1, "A message."},
@@ -471,9 +463,7 @@ TEST(MsgqueueTest, MsgRcvTypeBlocking) {
// Test msgsnd (most probably) blocking on a full queue.
TEST(MsgqueueTest, MsgSndBlocking) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
-
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgmax buf{1, ""}; // Has max amount of bytes.
const size_t msgCount = msgMnb / msgMax; // Number of messages that can be
@@ -512,8 +502,7 @@ TEST(MsgqueueTest, MsgSndBlocking) {
// Test removing a queue while a blocking msgsnd is executing.
TEST(MsgqueueTest, MsgSndRmWhileBlocking) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
// Number of messages that can be sent without blocking.
const size_t msgCount = msgMnb / msgMax;
@@ -549,8 +538,7 @@ TEST(MsgqueueTest, MsgSndRmWhileBlocking) {
// Test removing a queue while a blocking msgrcv is executing.
TEST(MsgqueueTest, MsgRcvRmWhileBlocking) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
ScopedThread t([&] {
// Because we're repeating on EINTR, msgsnd may race with msgctl(IPC_RMID)
@@ -568,8 +556,7 @@ TEST(MsgqueueTest, MsgRcvRmWhileBlocking) {
// Test a collection of msgsnd/msgrcv operations in different processes.
TEST(MsgqueueTest, MsgOpGeneral) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
- ASSERT_THAT(queue.get(), SyscallSucceeds());
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
// Create multiple sending/receiving threads that send messages back and
// forth. There's a matching recv for each send, so by the end of the test,
@@ -625,7 +612,7 @@ TEST(MsgqueueTest, MsgOpGeneral) {
void empty_sighandler(int sig, siginfo_t* info, void* context) {}
TEST(MsgqueueTest, InterruptRecv) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
char buf[64];
absl::Notification done, exit;
@@ -663,7 +650,7 @@ TEST(MsgqueueTest, InterruptRecv) {
}
TEST(MsgqueueTest, InterruptSend) {
- Queue queue(msgget(IPC_PRIVATE, 0600));
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
msgmax buf{1, ""};
// Number of messages that can be sent without blocking.
const size_t msgCount = msgMnb / msgMax;
@@ -708,6 +695,178 @@ TEST(MsgqueueTest, InterruptSend) {
t.Join();
}
+// Test msgctl with IPC_STAT option.
+TEST(MsgqueueTest, MsgCtlIpcStat) {
+ auto start = absl::Now();
+
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ const uid_t uid = getuid();
+ const gid_t gid = getgid();
+ const pid_t pid = getpid();
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+
+ EXPECT_EQ(ds.msg_perm.__key, IPC_PRIVATE);
+ EXPECT_EQ(ds.msg_perm.uid, uid);
+ EXPECT_EQ(ds.msg_perm.gid, gid);
+ EXPECT_EQ(ds.msg_perm.cuid, uid);
+ EXPECT_EQ(ds.msg_perm.cgid, gid);
+ EXPECT_EQ(ds.msg_perm.mode, 0600);
+
+ EXPECT_EQ(ds.msg_stime, 0);
+ EXPECT_EQ(ds.msg_rtime, 0);
+ EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start));
+
+ EXPECT_EQ(ds.msg_cbytes, 0);
+ EXPECT_EQ(ds.msg_qnum, 0);
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+ EXPECT_EQ(ds.msg_lspid, 0);
+ EXPECT_EQ(ds.msg_lrpid, 0);
+
+ // The timestamps only have a resolution of seconds; slow down so we actually
+ // see the timestamps change.
+ absl::SleepFor(absl::Seconds(1));
+ auto pre_send = absl::Now();
+
+ msgbuf buf{1, "A message."};
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+
+ EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send));
+ EXPECT_EQ(ds.msg_rtime, 0);
+ EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start));
+
+ EXPECT_EQ(ds.msg_cbytes, msgSize);
+ EXPECT_EQ(ds.msg_qnum, 1);
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+ EXPECT_EQ(ds.msg_lspid, pid);
+ EXPECT_EQ(ds.msg_lrpid, 0);
+
+ absl::SleepFor(absl::Seconds(1));
+ auto pre_receive = absl::Now();
+
+ ASSERT_THAT(msgrcv(queue.get(), &buf, sizeof(buf.mtext), 0, 0),
+ SyscallSucceedsWithValue(msgSize));
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+
+ EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send));
+ EXPECT_GE(ds.msg_rtime, absl::ToTimeT(pre_receive));
+ EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start));
+
+ EXPECT_EQ(ds.msg_cbytes, 0);
+ EXPECT_EQ(ds.msg_qnum, 0);
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+ EXPECT_EQ(ds.msg_lspid, pid);
+ EXPECT_EQ(ds.msg_lrpid, pid);
+}
+
+// Test msgctl with IPC_STAT option on a write-only queue.
+TEST(MsgqueueTest, MsgCtlIpcStatWriteOnly) {
+ // Drop CAP_IPC_OWNER which allows us to bypass permissions.
+ AutoCapability cap(CAP_IPC_OWNER, false);
+
+ Queue queue(msgget(IPC_PRIVATE, 0200));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds),
+ SyscallFailsWithErrno(EACCES));
+}
+
+// Test msgctl with IPC_SET option.
+TEST(MsgqueueTest, MsgCtlIpcSet) {
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_perm.mode, 0600);
+
+ ds.msg_perm.mode = 0777;
+ ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_perm.mode, 0777);
+}
+
+// Test increasing msg_qbytes beyond limit with IPC_SET.
+TEST(MsgqueueTest, MsgCtlIpcSetMaxBytes) {
+ // Drop CAP_SYS_RESOURCE which allows us to increase msg_qbytes beyond the
+ // system parameter MSGMNB.
+ AutoCapability cap(CAP_SYS_RESOURCE, false);
+
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+
+ ds.msg_qbytes = msgMnb - 10;
+ ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_qbytes, msgMnb - 10);
+
+ ds.msg_qbytes = msgMnb + 10;
+ EXPECT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallFailsWithErrno(EPERM));
+}
+
+// Test msgctl with IPC_INFO option.
+TEST(MsgqueueTest, MsgCtlIpcInfo) {
+ struct msginfo info;
+ ASSERT_THAT(msgctl(0, IPC_INFO, reinterpret_cast<struct msqid_ds*>(&info)),
+ SyscallSucceeds());
+
+ EXPECT_GT(info.msgmax, 0);
+ EXPECT_GT(info.msgmni, 0);
+ EXPECT_GT(info.msgmnb, 0);
+ EXPECT_EQ(info.msgpool, msgPool);
+ EXPECT_EQ(info.msgmap, msgMap);
+ EXPECT_EQ(info.msgssz, msgSsz);
+ EXPECT_EQ(info.msgtql, msgTql);
+}
+
+// Test msgctl with MSG_INFO option.
+TEST(MsgqueueTest, MsgCtlMsgInfo) {
+ struct msginfo info;
+ ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast<struct msqid_ds*>(&info)),
+ SyscallSucceeds());
+
+ EXPECT_GT(info.msgmax, 0);
+ EXPECT_GT(info.msgmni, 0);
+ EXPECT_GT(info.msgmnb, 0);
+ EXPECT_EQ(info.msgpool, 0); // Number of queues in the system.
+ EXPECT_EQ(info.msgmap, 0); // Total number of messages in all queues.
+ EXPECT_EQ(info.msgtql, 0); // Total number of bytes in all messages.
+ EXPECT_EQ(info.msgssz, msgSsz);
+
+ // Add a queue and a message.
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ msgbuf buf{1, "A message."};
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast<struct msqid_ds*>(&info)),
+ SyscallSucceeds());
+
+ EXPECT_GT(info.msgmax, 0);
+ EXPECT_GT(info.msgmni, 0);
+ EXPECT_GT(info.msgmnb, 0);
+ EXPECT_EQ(info.msgpool, 1); // Number of queues in the system.
+ EXPECT_EQ(info.msgmap, 1); // Total number of messages in all queues.
+ EXPECT_EQ(info.msgtql, msgSize); // Total number of bytes in all messages.
+ EXPECT_EQ(info.msgssz, msgSsz);
+}
+
} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/network_namespace.cc b/test/syscalls/linux/network_namespace.cc
index 1984feedd..aa9e1bc04 100644
--- a/test/syscalls/linux/network_namespace.cc
+++ b/test/syscalls/linux/network_namespace.cc
@@ -12,18 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <net/if.h>
-#include <sched.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/util/capability_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/socket_util.h"
-#include "test/util/test_util.h"
#include "test/util/thread_util.h"
namespace gvisor {
@@ -37,13 +28,7 @@ TEST(NetworkNamespaceTest, LoopbackExists) {
ASSERT_THAT(unshare(CLONE_NEWNET), SyscallSucceedsWithValue(0));
// TODO(gvisor.dev/issue/1833): Update this to test that only "lo" exists.
- // Check loopback device exists.
- int sock = socket(AF_INET, SOCK_DGRAM, 0);
- ASSERT_THAT(sock, SyscallSucceeds());
- struct ifreq ifr;
- strncpy(ifr.ifr_name, "lo", IFNAMSIZ);
- EXPECT_THAT(ioctl(sock, SIOCGIFINDEX, &ifr), SyscallSucceeds())
- << "lo cannot be found";
+ ASSERT_NE(ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()), 0);
});
}
diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc
index 98339277b..c706848fc 100644
--- a/test/syscalls/linux/packet_socket.cc
+++ b/test/syscalls/linux/packet_socket.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The gVisor Authors.
+// Copyright 2021 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.
@@ -12,48 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <arpa/inet.h>
-#include <ifaddrs.h>
-#include <linux/capability.h>
-#include <linux/if_arp.h>
-#include <linux/if_packet.h>
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/udp.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netpacket/packet.h>
#include <poll.h>
-#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
-#include <unistd.h>
+
+#include <limits>
#include "gtest/gtest.h"
-#include "absl/base/internal/endian.h"
-#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/socket_util.h"
-#include "test/util/test_util.h"
-
-// Some of these tests involve sending packets via AF_PACKET sockets and the
-// loopback interface. Because AF_PACKET circumvents so much of the networking
-// stack, Linux sees these packets as "martian", i.e. they claim to be to/from
-// localhost but don't have the usual associated data. Thus Linux drops them by
-// default. You can see where this happens by following the code at:
-//
-// - net/ipv4/ip_input.c:ip_rcv_finish, which calls
-// - net/ipv4/route.c:ip_route_input_noref, which calls
-// - net/ipv4/route.c:ip_route_input_slow, which finds and drops martian
-// packets.
-//
-// To tell Linux not to drop these packets, you need to tell it to accept our
-// funny packets (which are completely valid and correct, but lack associated
-// in-kernel data because we use AF_PACKET):
-//
-// echo 1 >> /proc/sys/net/ipv4/conf/lo/accept_local
-// echo 1 >> /proc/sys/net/ipv4/conf/lo/route_localnet
-//
-// These tests require CAP_NET_RAW to run.
namespace gvisor {
namespace testing {
@@ -61,496 +33,257 @@ namespace testing {
namespace {
using ::testing::AnyOf;
+using ::testing::Combine;
using ::testing::Eq;
+using ::testing::Values;
-constexpr char kMessage[] = "soweoneul malhaebwa";
-constexpr in_port_t kPort = 0x409c; // htons(40000)
-
-//
-// "Cooked" tests. Cooked AF_PACKET sockets do not contain link layer
-// headers, and provide link layer destination/source information via a
-// returned struct sockaddr_ll.
-//
-
-// Send kMessage via sock to loopback
-void SendUDPMessage(int sock) {
- struct sockaddr_in dest = {};
- dest.sin_port = kPort;
- dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- dest.sin_family = AF_INET;
- EXPECT_THAT(sendto(sock, kMessage, sizeof(kMessage), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(kMessage)));
-}
-
-// Send an IP packet and make sure ETH_P_<something else> doesn't pick it up.
-TEST(BasicCookedPacketTest, WrongType) {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
- ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, ETH_P_PUP),
- SyscallFailsWithErrno(EPERM));
- GTEST_SKIP();
- }
-
- FileDescriptor sock = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_PUP)));
-
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- SendUDPMessage(udp_sock.get());
-
- // Wait and make sure the socket never becomes readable.
- struct pollfd pfd = {};
- pfd.fd = sock.get();
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
-}
-
-// Tests for "cooked" (SOCK_DGRAM) packet(7) sockets.
-class CookedPacketTest : public ::testing::TestWithParam<int> {
+class PacketSocketCreationTest
+ : public ::testing::TestWithParam<std::tuple<int, int>> {
protected:
- // Creates a socket to be used in tests.
- void SetUp() override;
-
- // Closes the socket created by SetUp().
- void TearDown() override;
-
- // Gets the device index of the loopback device.
- int GetLoopbackIndex();
-
- // The socket used for both reading and writing.
- int socket_;
-};
-
-void CookedPacketTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
- ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
- SyscallFailsWithErrno(EPERM));
- GTEST_SKIP();
- }
-
- if (!IsRunningOnGvisor()) {
- FileDescriptor acceptLocal = ASSERT_NO_ERRNO_AND_VALUE(
- Open("/proc/sys/net/ipv4/conf/lo/accept_local", O_RDONLY));
- FileDescriptor routeLocalnet = ASSERT_NO_ERRNO_AND_VALUE(
- Open("/proc/sys/net/ipv4/conf/lo/route_localnet", O_RDONLY));
- char enabled;
- ASSERT_THAT(read(acceptLocal.get(), &enabled, 1),
- SyscallSucceedsWithValue(1));
- ASSERT_EQ(enabled, '1');
- ASSERT_THAT(read(routeLocalnet.get(), &enabled, 1),
- SyscallSucceedsWithValue(1));
- ASSERT_EQ(enabled, '1');
+ void SetUp() override {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ const auto [type, protocol] = GetParam();
+ ASSERT_THAT(socket(AF_PACKET, type, htons(protocol)),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP() << "Missing packet socket capability";
+ }
}
+};
- ASSERT_THAT(socket_ = socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
- SyscallSucceeds());
+TEST_P(PacketSocketCreationTest, Create) {
+ const auto [type, protocol] = GetParam();
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, type, htons(protocol)));
+ EXPECT_GE(fd.get(), 0);
}
-void CookedPacketTest::TearDown() {
- // TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
- EXPECT_THAT(close(socket_), SyscallSucceeds());
- }
-}
+INSTANTIATE_TEST_SUITE_P(AllPacketSocketTests, PacketSocketCreationTest,
+ Combine(Values(SOCK_DGRAM, SOCK_RAW),
+ Values(0, 1, 255, ETH_P_IP, ETH_P_IPV6,
+ std::numeric_limits<uint16_t>::max())));
-int CookedPacketTest::GetLoopbackIndex() {
- struct ifreq ifr;
- snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
- EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
- return ifr.ifr_ifindex;
-}
+class PacketSocketTest : public ::testing::TestWithParam<int> {
+ protected:
+ void SetUp() override {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ ASSERT_THAT(socket(AF_PACKET, GetParam(), 0),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP() << "Missing packet socket capability";
+ }
-// Receive and verify the message via packet socket on interface.
-void ReceiveMessage(int sock, int ifindex) {
- // Wait for the socket to become readable.
- struct pollfd pfd = {};
- pfd.fd = sock;
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 2000), SyscallSucceedsWithValue(1));
-
- // Read and verify the data.
- constexpr size_t packet_size =
- sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
- char buf[64];
- struct sockaddr_ll src = {};
- socklen_t src_len = sizeof(src);
- ASSERT_THAT(recvfrom(sock, buf, sizeof(buf), 0,
- reinterpret_cast<struct sockaddr*>(&src), &src_len),
- SyscallSucceedsWithValue(packet_size));
-
- // sockaddr_ll ends with an 8 byte physical address field, but ethernet
- // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
- // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
- // sizeof(sockaddr_ll).
- ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
-
- // Verify the source address.
- EXPECT_EQ(src.sll_family, AF_PACKET);
- EXPECT_EQ(src.sll_ifindex, ifindex);
- EXPECT_EQ(src.sll_halen, ETH_ALEN);
- EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
- // This came from the loopback device, so the address is all 0s.
- for (int i = 0; i < src.sll_halen; i++) {
- EXPECT_EQ(src.sll_addr[i], 0);
+ socket_ = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, GetParam(), 0));
}
- // Verify the IP header. We memcpy to deal with pointer aligment.
- struct iphdr ip = {};
- memcpy(&ip, buf, sizeof(ip));
- EXPECT_EQ(ip.ihl, 5);
- EXPECT_EQ(ip.version, 4);
- EXPECT_EQ(ip.tot_len, htons(packet_size));
- EXPECT_EQ(ip.protocol, IPPROTO_UDP);
- EXPECT_EQ(ip.daddr, htonl(INADDR_LOOPBACK));
- EXPECT_EQ(ip.saddr, htonl(INADDR_LOOPBACK));
-
- // Verify the UDP header. We memcpy to deal with pointer aligment.
- struct udphdr udp = {};
- memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
- EXPECT_EQ(udp.dest, kPort);
- EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
-
- // Verify the payload.
- char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
- EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
-}
-
-// Receive via a packet socket.
-TEST_P(CookedPacketTest, Receive) {
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- SendUDPMessage(udp_sock.get());
-
- // Receive and verify the data.
- int loopback_index = GetLoopbackIndex();
- ReceiveMessage(socket_, loopback_index);
-}
+ FileDescriptor socket_;
+};
-// Send via a packet socket.
-TEST_P(CookedPacketTest, Send) {
- // Let's send a UDP packet and receive it using a regular UDP socket.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- struct sockaddr_in bind_addr = {};
- bind_addr.sin_family = AF_INET;
- bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- bind_addr.sin_port = kPort;
- ASSERT_THAT(
- bind(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- SyscallSucceeds());
-
- // Set up the destination physical address.
- struct sockaddr_ll dest = {};
- dest.sll_family = AF_PACKET;
- dest.sll_halen = ETH_ALEN;
- dest.sll_ifindex = GetLoopbackIndex();
- dest.sll_protocol = htons(ETH_P_IP);
- // We're sending to the loopback device, so the address is all 0s.
- memset(dest.sll_addr, 0x00, ETH_ALEN);
-
- // Set up the IP header.
- struct iphdr iphdr = {0};
- iphdr.ihl = 5;
- iphdr.version = 4;
- iphdr.tos = 0;
- iphdr.tot_len =
- htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage));
- // Get a pseudo-random ID. If we clash with an in-use ID the test will fail,
- // but we have no way of getting an ID we know to be good.
- srand(*reinterpret_cast<unsigned int*>(&iphdr));
- iphdr.id = rand();
- // Linux sets this bit ("do not fragment") for small packets.
- iphdr.frag_off = 1 << 6;
- iphdr.ttl = 64;
- iphdr.protocol = IPPROTO_UDP;
- iphdr.daddr = htonl(INADDR_LOOPBACK);
- iphdr.saddr = htonl(INADDR_LOOPBACK);
- iphdr.check = IPChecksum(iphdr);
-
- // Set up the UDP header.
- struct udphdr udphdr = {};
- udphdr.source = kPort;
- udphdr.dest = kPort;
- udphdr.len = htons(sizeof(udphdr) + sizeof(kMessage));
- udphdr.check = UDPChecksum(iphdr, udphdr, kMessage, sizeof(kMessage));
-
- // Copy both headers and the payload into our packet buffer.
- char send_buf[sizeof(iphdr) + sizeof(udphdr) + sizeof(kMessage)];
- memcpy(send_buf, &iphdr, sizeof(iphdr));
- memcpy(send_buf + sizeof(iphdr), &udphdr, sizeof(udphdr));
- memcpy(send_buf + sizeof(iphdr) + sizeof(udphdr), kMessage, sizeof(kMessage));
-
- // We don't implement writing to packet sockets on gVisor.
- if (IsRunningOnGvisor()) {
- ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallFailsWithErrno(EINVAL));
- GTEST_SKIP();
+TEST_P(PacketSocketTest, GetSockName) {
+ {
+ // First check the local address of an unbound packet socket.
+ sockaddr_ll addr;
+ socklen_t addrlen = sizeof(addr);
+ ASSERT_THAT(getsockname(socket_.get(), reinterpret_cast<sockaddr*>(&addr),
+ &addrlen),
+ SyscallSucceeds());
+ // sockaddr_ll ends with an 8 byte physical address field, but only the
+ // bytes that are used in the sockaddr_ll.sll_addr field are included in the
+ // address length. Seems Linux used to return the size of sockaddr_ll, but
+ // https://github.com/torvalds/linux/commit/0fb375fb9b93b7d822debc6a734052337ccfdb1f
+ // changed things to only return `sizeof(sockaddr_ll) + sll.sll_addr`.
+ ASSERT_THAT(addrlen, AnyOf(Eq(sizeof(addr)),
+ Eq(sizeof(addr) - sizeof(addr.sll_addr))));
+ EXPECT_EQ(addr.sll_family, AF_PACKET);
+ EXPECT_EQ(addr.sll_ifindex, 0);
+ if (IsRunningOnGvisor() && !IsRunningWithHostinet()) {
+ // TODO(https://gvisor.dev/issue/6530): Do not assume all interfaces have
+ // an ethernet address.
+ EXPECT_EQ(addr.sll_halen, ETH_ALEN);
+ } else {
+ EXPECT_EQ(addr.sll_halen, 0);
+ }
+ EXPECT_EQ(ntohs(addr.sll_protocol), 0);
+ EXPECT_EQ(addr.sll_hatype, 0);
}
-
- // Send it.
- ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(send_buf)));
-
- // Wait for the packet to become available on both sockets.
- struct pollfd pfd = {};
- pfd.fd = udp_sock.get();
- pfd.events = POLLIN;
- ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
- pfd.fd = socket_;
- pfd.events = POLLIN;
- ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
-
- // Receive on the packet socket.
- char recv_buf[sizeof(send_buf)];
- ASSERT_THAT(recv(socket_, recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
- ASSERT_EQ(memcmp(recv_buf, send_buf, sizeof(send_buf)), 0);
-
- // Receive on the UDP socket.
- struct sockaddr_in src;
- socklen_t src_len = sizeof(src);
- ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT,
- reinterpret_cast<struct sockaddr*>(&src), &src_len),
- SyscallSucceedsWithValue(sizeof(kMessage)));
- // Check src and payload.
- EXPECT_EQ(strncmp(recv_buf, kMessage, sizeof(kMessage)), 0);
- EXPECT_EQ(src.sin_family, AF_INET);
- EXPECT_EQ(src.sin_port, kPort);
- EXPECT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
-}
-
-// Bind and receive via packet socket.
-TEST_P(CookedPacketTest, BindReceive) {
- 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(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ // Next we bind the socket to loopback before checking the local address.
+ const sockaddr_ll bind_addr = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_IP),
+ .sll_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()),
+ };
+ ASSERT_THAT(bind(socket_.get(), reinterpret_cast<const sockaddr*>(&bind_addr),
sizeof(bind_addr)),
SyscallSucceeds());
-
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- SendUDPMessage(udp_sock.get());
-
- // Receive and verify the data.
- ReceiveMessage(socket_, bind_addr.sll_ifindex);
-}
-
-// Double Bind socket.
-TEST_P(CookedPacketTest, DoubleBindSucceeds) {
- 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(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- 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.
- SyscallSucceeds());
-}
-
-// Bind and verify we do not receive data on interface which is not bound
-TEST_P(CookedPacketTest, BindDrop) {
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
-
- struct ifaddrs* if_addr_list = nullptr;
- auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
-
- ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
-
- // Get interface other than loopback.
- struct ifreq ifr = {};
- for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
- if (strcmp(i->ifa_name, "lo") != 0) {
- strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
- break;
+ {
+ sockaddr_ll addr;
+ socklen_t addrlen = sizeof(addr);
+ ASSERT_THAT(getsockname(socket_.get(), reinterpret_cast<sockaddr*>(&addr),
+ &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(addrlen,
+ AnyOf(Eq(sizeof(addr)),
+ Eq(sizeof(addr) - sizeof(addr.sll_addr) + ETH_ALEN)));
+ EXPECT_EQ(addr.sll_family, AF_PACKET);
+ EXPECT_EQ(addr.sll_ifindex, bind_addr.sll_ifindex);
+ EXPECT_EQ(addr.sll_halen, ETH_ALEN);
+ // Bound to loopback which has the all zeroes address.
+ for (int i = 0; i < addr.sll_halen; ++i) {
+ EXPECT_EQ(addr.sll_addr[i], 0) << "byte mismatch @ idx = " << i;
+ }
+ EXPECT_EQ(ntohs(addr.sll_protocol), htons(addr.sll_protocol));
+ if (IsRunningOnGvisor() && !IsRunningWithHostinet()) {
+ // TODO(https://gvisor.dev/issue/6621): Support populating sll_hatype.
+ EXPECT_EQ(addr.sll_hatype, 0);
+ } else {
+ EXPECT_EQ(addr.sll_hatype, ARPHRD_LOOPBACK);
}
}
-
- // Skip if no interface is available other than loopback.
- if (strlen(ifr.ifr_name) == 0) {
- GTEST_SKIP();
- }
-
- // Get interface index.
- EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
-
- // Bind to packet socket requires only family, protocol and ifindex.
- struct sockaddr_ll bind_addr = {};
- bind_addr.sll_family = AF_PACKET;
- bind_addr.sll_protocol = htons(GetParam());
- bind_addr.sll_ifindex = ifr.ifr_ifindex;
-
- ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- SyscallSucceeds());
-
- // Send to loopback interface.
- struct sockaddr_in dest = {};
- dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- dest.sin_family = AF_INET;
- dest.sin_port = kPort;
- EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(kMessage)));
-
- // Wait and make sure the socket never receives any data.
- struct pollfd pfd = {};
- pfd.fd = socket_;
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
}
-// Verify that we receive outbound packets. This test requires at least one
-// non loopback interface so that we can actually capture an outgoing packet.
-TEST_P(CookedPacketTest, ReceiveOutbound) {
- // Only ETH_P_ALL sockets can receive outbound packets on linux.
- SKIP_IF(GetParam() != ETH_P_ALL);
+TEST_P(PacketSocketTest, RebindProtocol) {
+ const bool kEthHdrIncluded = GetParam() == SOCK_RAW;
+
+ sockaddr_in udp_bind_addr = {
+ .sin_family = AF_INET,
+ .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)},
+ };
- // Let's use a simple IP payload: a UDP datagram.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
-
- struct ifaddrs* if_addr_list = nullptr;
- auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
-
- ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
-
- // Get interface other than loopback.
- struct ifreq ifr = {};
- for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
- if (strcmp(i->ifa_name, "lo") != 0) {
- strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
- break;
- }
- }
-
- // Skip if no interface is available other than loopback.
- if (strlen(ifr.ifr_name) == 0) {
- GTEST_SKIP();
+ {
+ // Bind the socket so that we have something to send packets to.
+ //
+ // If we didn't do this, the UDP packets we send will be responded to with
+ // ICMP Destination Port Unreachable errors.
+ ASSERT_THAT(
+ bind(udp_sock.get(), reinterpret_cast<const sockaddr*>(&udp_bind_addr),
+ sizeof(udp_bind_addr)),
+ SyscallSucceeds());
+ socklen_t addrlen = sizeof(udp_bind_addr);
+ ASSERT_THAT(
+ getsockname(udp_sock.get(), reinterpret_cast<sockaddr*>(&udp_bind_addr),
+ &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(addrlen, sizeof(udp_bind_addr));
}
- // Get interface index and name.
- EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
- int ifindex = ifr.ifr_ifindex;
-
- constexpr int kMACSize = 6;
- char hwaddr[kMACSize];
- // Get interface address.
- ASSERT_THAT(ioctl(socket_, SIOCGIFHWADDR, &ifr), SyscallSucceeds());
- ASSERT_THAT(ifr.ifr_hwaddr.sa_family,
- AnyOf(Eq(ARPHRD_NONE), Eq(ARPHRD_ETHER)));
- memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, kMACSize);
-
- // Just send it to the google dns server 8.8.8.8. It's UDP we don't care
- // if it actually gets to the DNS Server we just want to see that we receive
- // it on our AF_PACKET socket.
- //
- // NOTE: We just want to pick an IP that is non-local to avoid having to
- // handle ARP as this should cause the UDP packet to be sent to the default
- // gateway configured for the system under test. Otherwise the only packet we
- // will see is the ARP query unless we picked an IP which will actually
- // resolve. The test is a bit brittle but this was the best compromise for
- // now.
- struct sockaddr_in dest = {};
- ASSERT_EQ(inet_pton(AF_INET, "8.8.8.8", &dest.sin_addr.s_addr), 1);
- dest.sin_family = AF_INET;
- dest.sin_port = kPort;
- EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(kMessage)));
-
- // Wait and make sure the socket receives the data.
- struct pollfd pfd = {};
- pfd.fd = socket_;
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(1));
-
- // Now read and check that the packet is the one we just sent.
- // Read and verify the data.
- constexpr size_t packet_size =
- sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
- char buf[64];
- struct sockaddr_ll src = {};
- socklen_t src_len = sizeof(src);
- ASSERT_THAT(recvfrom(socket_, buf, sizeof(buf), 0,
- reinterpret_cast<struct sockaddr*>(&src), &src_len),
- SyscallSucceedsWithValue(packet_size));
-
- // sockaddr_ll ends with an 8 byte physical address field, but ethernet
- // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
- // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
- // sizeof(sockaddr_ll).
- ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
-
- // Verify the source address.
- EXPECT_EQ(src.sll_family, AF_PACKET);
- EXPECT_EQ(src.sll_ifindex, ifindex);
- EXPECT_EQ(src.sll_halen, ETH_ALEN);
- EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
- EXPECT_EQ(src.sll_pkttype, PACKET_OUTGOING);
- // Verify the link address of the interface matches that of the non
- // non loopback interface address we stored above.
- for (int i = 0; i < src.sll_halen; i++) {
- EXPECT_EQ(src.sll_addr[i], hwaddr[i]);
- }
+ const int loopback_index = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+
+ auto send_udp_message = [&](const uint64_t v) {
+ ASSERT_THAT(
+ sendto(udp_sock.get(), reinterpret_cast<const char*>(&v), sizeof(v),
+ 0 /* flags */, reinterpret_cast<const sockaddr*>(&udp_bind_addr),
+ sizeof(udp_bind_addr)),
+ SyscallSucceeds());
+ };
+
+ auto bind_to_network_protocol = [&](uint16_t protocol) {
+ const sockaddr_ll packet_bind_addr = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(protocol),
+ .sll_ifindex = loopback_index,
+ };
+
+ ASSERT_THAT(bind(socket_.get(),
+ reinterpret_cast<const sockaddr*>(&packet_bind_addr),
+ sizeof(packet_bind_addr)),
+ SyscallSucceeds());
+ };
+
+ auto test_recv = [&, this](const uint64_t v) {
+ constexpr int kInfiniteTimeout = -1;
+ pollfd pfd = {
+ .fd = socket_.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfiniteTimeout),
+ SyscallSucceedsWithValue(1));
- // Verify the IP header.
- struct iphdr ip = {};
- memcpy(&ip, buf, sizeof(ip));
- EXPECT_EQ(ip.ihl, 5);
- EXPECT_EQ(ip.version, 4);
- EXPECT_EQ(ip.tot_len, htons(packet_size));
- EXPECT_EQ(ip.protocol, IPPROTO_UDP);
- EXPECT_EQ(ip.daddr, dest.sin_addr.s_addr);
- EXPECT_NE(ip.saddr, htonl(INADDR_LOOPBACK));
-
- // Verify the UDP header.
- struct udphdr udp = {};
- memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
- EXPECT_EQ(udp.dest, kPort);
- EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
-
- // Verify the payload.
- char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
- EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
-}
+ struct {
+ ethhdr eth;
+ iphdr ip;
+ udphdr udp;
+ uint64_t payload;
+ char unused;
+ } ABSL_ATTRIBUTE_PACKED read_pkt;
+ sockaddr_ll src;
+ socklen_t src_len = sizeof(src);
+
+ char* buf = reinterpret_cast<char*>(&read_pkt);
+ size_t buflen = sizeof(read_pkt);
+ size_t expected_read_len = sizeof(read_pkt) - sizeof(read_pkt.unused);
+ if (!kEthHdrIncluded) {
+ buf += sizeof(read_pkt.eth);
+ buflen -= sizeof(read_pkt.eth);
+ expected_read_len -= sizeof(read_pkt.eth);
+ }
-// Bind with invalid address.
-TEST_P(CookedPacketTest, BindFail) {
- // Null address.
- ASSERT_THAT(
- bind(socket_, nullptr, sizeof(struct sockaddr)),
- AnyOf(SyscallFailsWithErrno(EFAULT), SyscallFailsWithErrno(EINVAL)));
-
- // Address of size 1.
- uint8_t addr = 0;
- ASSERT_THAT(
- bind(socket_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
- SyscallFailsWithErrno(EINVAL));
+ ASSERT_THAT(recvfrom(socket_.get(), buf, buflen, 0,
+ reinterpret_cast<sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(expected_read_len));
+ // sockaddr_ll ends with an 8 byte physical address field, but ethernet
+ // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
+ // here, but returns sizeof(sockaddr_ll) since
+ // https://github.com/torvalds/linux/commit/b2cf86e1563e33a14a1c69b3e508d15dc12f804c.
+ ASSERT_THAT(src_len,
+ AnyOf(Eq(sizeof(src)),
+ Eq(sizeof(src) - sizeof(src.sll_addr) + ETH_ALEN)));
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(src.sll_ifindex, loopback_index);
+ EXPECT_EQ(src.sll_halen, ETH_ALEN);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ // This came from the loopback device, so the address is all 0s.
+ constexpr uint8_t allZeroesMAC[ETH_ALEN] = {};
+ EXPECT_EQ(memcmp(src.sll_addr, allZeroesMAC, sizeof(allZeroesMAC)), 0);
+ if (kEthHdrIncluded) {
+ EXPECT_EQ(memcmp(read_pkt.eth.h_dest, allZeroesMAC, sizeof(allZeroesMAC)),
+ 0);
+ EXPECT_EQ(
+ memcmp(read_pkt.eth.h_source, allZeroesMAC, sizeof(allZeroesMAC)), 0);
+ EXPECT_EQ(ntohs(read_pkt.eth.h_proto), ETH_P_IP);
+ }
+ // IHL hold the size of the header in 4 byte units.
+ EXPECT_EQ(read_pkt.ip.ihl, sizeof(iphdr) / 4);
+ EXPECT_EQ(read_pkt.ip.version, IPVERSION);
+ const uint16_t ip_pkt_size =
+ sizeof(read_pkt) - sizeof(read_pkt.eth) - sizeof(read_pkt.unused);
+ EXPECT_EQ(ntohs(read_pkt.ip.tot_len), ip_pkt_size);
+ EXPECT_EQ(read_pkt.ip.protocol, IPPROTO_UDP);
+ EXPECT_EQ(ntohl(read_pkt.ip.daddr), INADDR_LOOPBACK);
+ EXPECT_EQ(ntohl(read_pkt.ip.saddr), INADDR_LOOPBACK);
+ EXPECT_EQ(read_pkt.udp.source, udp_bind_addr.sin_port);
+ EXPECT_EQ(read_pkt.udp.dest, udp_bind_addr.sin_port);
+ EXPECT_EQ(ntohs(read_pkt.udp.len), ip_pkt_size - sizeof(read_pkt.ip));
+ EXPECT_EQ(read_pkt.payload, v);
+ };
+
+ // The packet socket is not bound to IPv4 so we should not receive the sent
+ // message.
+ uint64_t counter = 0;
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+
+ // Bind to IPv4 and expect to receive the UDP packet we send after binding.
+ ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(ETH_P_IP));
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+ ASSERT_NO_FATAL_FAILURE(test_recv(counter));
+
+ // Bind the packet socket to a random protocol.
+ ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(255));
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+
+ // Bind back to IPv4 and expect to the UDP packet we send after binding
+ // back to IPv4.
+ ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(ETH_P_IP));
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+ ASSERT_NO_FATAL_FAILURE(test_recv(counter));
+
+ // A zero valued protocol number should not change the bound network protocol.
+ ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(0));
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+ ASSERT_NO_FATAL_FAILURE(test_recv(counter));
}
-INSTANTIATE_TEST_SUITE_P(AllInetTests, CookedPacketTest,
- ::testing::Values(ETH_P_IP, ETH_P_ALL));
+INSTANTIATE_TEST_SUITE_P(AllPacketSocketTests, PacketSocketTest,
+ Values(SOCK_DGRAM, SOCK_RAW));
} // namespace
diff --git a/test/syscalls/linux/packet_socket_dgram.cc b/test/syscalls/linux/packet_socket_dgram.cc
new file mode 100644
index 000000000..9515a48f2
--- /dev/null
+++ b/test/syscalls/linux/packet_socket_dgram.cc
@@ -0,0 +1,550 @@
+// 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 <ifaddrs.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netpacket/packet.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/endian.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/capability_util.h"
+#include "test/util/cleanup.h"
+#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
+#include "test/util/test_util.h"
+
+// Some of these tests involve sending packets via AF_PACKET sockets and the
+// loopback interface. Because AF_PACKET circumvents so much of the networking
+// stack, Linux sees these packets as "martian", i.e. they claim to be to/from
+// localhost but don't have the usual associated data. Thus Linux drops them by
+// default. You can see where this happens by following the code at:
+//
+// - net/ipv4/ip_input.c:ip_rcv_finish, which calls
+// - net/ipv4/route.c:ip_route_input_noref, which calls
+// - net/ipv4/route.c:ip_route_input_slow, which finds and drops martian
+// packets.
+//
+// To tell Linux not to drop these packets, you need to tell it to accept our
+// funny packets (which are completely valid and correct, but lack associated
+// in-kernel data because we use AF_PACKET):
+//
+// echo 1 >> /proc/sys/net/ipv4/conf/lo/accept_local
+// echo 1 >> /proc/sys/net/ipv4/conf/lo/route_localnet
+//
+// These tests require CAP_NET_RAW to run.
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+using ::testing::AnyOf;
+using ::testing::Eq;
+
+constexpr char kMessage[] = "soweoneul malhaebwa";
+constexpr in_port_t kPort = 0x409c; // htons(40000)
+
+//
+// "Cooked" tests. Cooked AF_PACKET sockets do not contain link layer
+// headers, and provide link layer destination/source information via a
+// returned struct sockaddr_ll.
+//
+
+// Send kMessage via sock to loopback
+void SendUDPMessage(int sock) {
+ struct sockaddr_in dest = {};
+ dest.sin_port = kPort;
+ dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ dest.sin_family = AF_INET;
+ EXPECT_THAT(sendto(sock, kMessage, sizeof(kMessage), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+}
+
+// Send an IP packet and make sure ETH_P_<something else> doesn't pick it up.
+TEST(BasicCookedPacketTest, WrongType) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, ETH_P_PUP),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP();
+ }
+
+ FileDescriptor sock = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_PUP)));
+
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ SendUDPMessage(udp_sock.get());
+
+ // Wait and make sure the socket never becomes readable.
+ struct pollfd pfd = {};
+ pfd.fd = sock.get();
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
+}
+
+// Tests for "cooked" (SOCK_DGRAM) packet(7) sockets.
+class CookedPacketTest : public ::testing::TestWithParam<int> {
+ protected:
+ // Creates a socket to be used in tests.
+ void SetUp() override;
+
+ // Closes the socket created by SetUp().
+ void TearDown() override;
+
+ // Gets the device index of the loopback device.
+ int GetLoopbackIndex();
+
+ // The socket used for both reading and writing.
+ int socket_;
+};
+
+void CookedPacketTest::SetUp() {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP();
+ }
+
+ if (!IsRunningOnGvisor()) {
+ FileDescriptor acceptLocal = ASSERT_NO_ERRNO_AND_VALUE(
+ Open("/proc/sys/net/ipv4/conf/lo/accept_local", O_RDONLY));
+ FileDescriptor routeLocalnet = ASSERT_NO_ERRNO_AND_VALUE(
+ Open("/proc/sys/net/ipv4/conf/lo/route_localnet", O_RDONLY));
+ char enabled;
+ ASSERT_THAT(read(acceptLocal.get(), &enabled, 1),
+ SyscallSucceedsWithValue(1));
+ ASSERT_EQ(enabled, '1');
+ ASSERT_THAT(read(routeLocalnet.get(), &enabled, 1),
+ SyscallSucceedsWithValue(1));
+ ASSERT_EQ(enabled, '1');
+ }
+
+ ASSERT_THAT(socket_ = socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
+ SyscallSucceeds());
+}
+
+void CookedPacketTest::TearDown() {
+ // TearDown will be run even if we skip the test.
+ if (ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ EXPECT_THAT(close(socket_), SyscallSucceeds());
+ }
+}
+
+int CookedPacketTest::GetLoopbackIndex() {
+ int v = EXPECT_NO_ERRNO_AND_VALUE(gvisor::testing::GetLoopbackIndex());
+ EXPECT_NE(v, 0);
+ return v;
+}
+
+// Receive and verify the message via packet socket on interface.
+void ReceiveMessage(int sock, int ifindex) {
+ // Wait for the socket to become readable.
+ struct pollfd pfd = {};
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 2000), SyscallSucceedsWithValue(1));
+
+ // Read and verify the data.
+ constexpr size_t packet_size =
+ sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
+ char buf[64];
+ struct sockaddr_ll src = {};
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(sock, buf, sizeof(buf), 0,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(packet_size));
+
+ // sockaddr_ll ends with an 8 byte physical address field, but ethernet
+ // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
+ // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
+ // sizeof(sockaddr_ll).
+ ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
+
+ // Verify the source address.
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(src.sll_ifindex, ifindex);
+ EXPECT_EQ(src.sll_halen, ETH_ALEN);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ // This came from the loopback device, so the address is all 0s.
+ for (int i = 0; i < src.sll_halen; i++) {
+ EXPECT_EQ(src.sll_addr[i], 0);
+ }
+
+ // Verify the IP header. We memcpy to deal with pointer aligment.
+ struct iphdr ip = {};
+ memcpy(&ip, buf, sizeof(ip));
+ EXPECT_EQ(ip.ihl, 5);
+ EXPECT_EQ(ip.version, 4);
+ EXPECT_EQ(ip.tot_len, htons(packet_size));
+ EXPECT_EQ(ip.protocol, IPPROTO_UDP);
+ EXPECT_EQ(ip.daddr, htonl(INADDR_LOOPBACK));
+ EXPECT_EQ(ip.saddr, htonl(INADDR_LOOPBACK));
+
+ // Verify the UDP header. We memcpy to deal with pointer aligment.
+ struct udphdr udp = {};
+ memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
+ EXPECT_EQ(udp.dest, kPort);
+ EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
+
+ // Verify the payload.
+ char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
+ EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
+}
+
+// Receive via a packet socket.
+TEST_P(CookedPacketTest, Receive) {
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ SendUDPMessage(udp_sock.get());
+
+ // Receive and verify the data.
+ int loopback_index = GetLoopbackIndex();
+ ReceiveMessage(socket_, loopback_index);
+}
+
+// Send via a packet socket.
+TEST_P(CookedPacketTest, Send) {
+ // Let's send a UDP packet and receive it using a regular UDP socket.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ struct sockaddr_in bind_addr = {};
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ bind_addr.sin_port = kPort;
+ ASSERT_THAT(
+ bind(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // Set up the destination physical address.
+ struct sockaddr_ll dest = {};
+ dest.sll_family = AF_PACKET;
+ dest.sll_halen = ETH_ALEN;
+ dest.sll_ifindex = GetLoopbackIndex();
+ dest.sll_protocol = htons(ETH_P_IP);
+ // We're sending to the loopback device, so the address is all 0s.
+ memset(dest.sll_addr, 0x00, ETH_ALEN);
+
+ // Set up the IP header.
+ struct iphdr iphdr = {0};
+ iphdr.ihl = 5;
+ iphdr.version = 4;
+ iphdr.tos = 0;
+ iphdr.tot_len =
+ htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage));
+ // Get a pseudo-random ID. If we clash with an in-use ID the test will fail,
+ // but we have no way of getting an ID we know to be good.
+ srand(*reinterpret_cast<unsigned int*>(&iphdr));
+ iphdr.id = rand();
+ // Linux sets this bit ("do not fragment") for small packets.
+ iphdr.frag_off = 1 << 6;
+ iphdr.ttl = 64;
+ iphdr.protocol = IPPROTO_UDP;
+ iphdr.daddr = htonl(INADDR_LOOPBACK);
+ iphdr.saddr = htonl(INADDR_LOOPBACK);
+ iphdr.check = IPChecksum(iphdr);
+
+ // Set up the UDP header.
+ struct udphdr udphdr = {};
+ udphdr.source = kPort;
+ udphdr.dest = kPort;
+ udphdr.len = htons(sizeof(udphdr) + sizeof(kMessage));
+ udphdr.check = UDPChecksum(iphdr, udphdr, kMessage, sizeof(kMessage));
+
+ // Copy both headers and the payload into our packet buffer.
+ char send_buf[sizeof(iphdr) + sizeof(udphdr) + sizeof(kMessage)];
+ memcpy(send_buf, &iphdr, sizeof(iphdr));
+ memcpy(send_buf + sizeof(iphdr), &udphdr, sizeof(udphdr));
+ memcpy(send_buf + sizeof(iphdr) + sizeof(udphdr), kMessage, sizeof(kMessage));
+
+ // Send it.
+ ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(send_buf)));
+
+ // Wait for the packet to become available on both sockets.
+ struct pollfd pfd = {};
+ pfd.fd = udp_sock.get();
+ pfd.events = POLLIN;
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
+ pfd.fd = socket_;
+ pfd.events = POLLIN;
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
+
+ // Receive on the packet socket.
+ char recv_buf[sizeof(send_buf)];
+ ASSERT_THAT(recv(socket_, recv_buf, sizeof(recv_buf), 0),
+ SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_EQ(memcmp(recv_buf, send_buf, sizeof(send_buf)), 0);
+
+ // Receive on the UDP socket.
+ struct sockaddr_in src;
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+ // Check src and payload.
+ EXPECT_EQ(strncmp(recv_buf, kMessage, sizeof(kMessage)), 0);
+ EXPECT_EQ(src.sin_family, AF_INET);
+ EXPECT_EQ(src.sin_port, kPort);
+ EXPECT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
+}
+
+// Bind and receive via packet socket.
+TEST_P(CookedPacketTest, BindReceive) {
+ 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(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ SendUDPMessage(udp_sock.get());
+
+ // Receive and verify the data.
+ ReceiveMessage(socket_, bind_addr.sll_ifindex);
+}
+
+// Double Bind socket.
+TEST_P(CookedPacketTest, DoubleBindSucceeds) {
+ 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(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ 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.
+ SyscallSucceeds());
+}
+
+// Bind and verify we do not receive data on interface which is not bound
+TEST_P(CookedPacketTest, BindDrop) {
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+
+ struct ifaddrs* if_addr_list = nullptr;
+ auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
+
+ ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
+
+ // Get interface other than loopback.
+ struct ifreq ifr = {};
+ for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
+ if (strcmp(i->ifa_name, "lo") != 0) {
+ strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
+ break;
+ }
+ }
+
+ // Skip if no interface is available other than loopback.
+ if (strlen(ifr.ifr_name) == 0) {
+ GTEST_SKIP();
+ }
+
+ // Get interface index.
+ EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
+ EXPECT_NE(ifr.ifr_ifindex, 0);
+
+ // Bind to packet socket requires only family, protocol and ifindex.
+ struct sockaddr_ll bind_addr = {};
+ bind_addr.sll_family = AF_PACKET;
+ bind_addr.sll_protocol = htons(GetParam());
+ bind_addr.sll_ifindex = ifr.ifr_ifindex;
+
+ ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // Send to loopback interface.
+ struct sockaddr_in dest = {};
+ dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ dest.sin_family = AF_INET;
+ dest.sin_port = kPort;
+ EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+
+ // Wait and make sure the socket never receives any data.
+ struct pollfd pfd = {};
+ pfd.fd = socket_;
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
+}
+
+// Verify that we receive outbound packets. This test requires at least one
+// non loopback interface so that we can actually capture an outgoing packet.
+TEST_P(CookedPacketTest, ReceiveOutbound) {
+ // Only ETH_P_ALL sockets can receive outbound packets on linux.
+ SKIP_IF(GetParam() != ETH_P_ALL);
+
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+
+ struct ifaddrs* if_addr_list = nullptr;
+ auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
+
+ ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
+
+ // Get interface other than loopback.
+ struct ifreq ifr = {};
+ for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
+ if (strcmp(i->ifa_name, "lo") != 0) {
+ strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
+ break;
+ }
+ }
+
+ // Skip if no interface is available other than loopback.
+ if (strlen(ifr.ifr_name) == 0) {
+ GTEST_SKIP();
+ }
+
+ // Get interface index and name.
+ EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
+ EXPECT_NE(ifr.ifr_ifindex, 0);
+ int ifindex = ifr.ifr_ifindex;
+
+ constexpr int kMACSize = 6;
+ char hwaddr[kMACSize];
+ // Get interface address.
+ ASSERT_THAT(ioctl(socket_, SIOCGIFHWADDR, &ifr), SyscallSucceeds());
+ ASSERT_THAT(ifr.ifr_hwaddr.sa_family,
+ AnyOf(Eq(ARPHRD_NONE), Eq(ARPHRD_ETHER)));
+ memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, kMACSize);
+
+ // Just send it to the google dns server 8.8.8.8. It's UDP we don't care
+ // if it actually gets to the DNS Server we just want to see that we receive
+ // it on our AF_PACKET socket.
+ //
+ // NOTE: We just want to pick an IP that is non-local to avoid having to
+ // handle ARP as this should cause the UDP packet to be sent to the default
+ // gateway configured for the system under test. Otherwise the only packet we
+ // will see is the ARP query unless we picked an IP which will actually
+ // resolve. The test is a bit brittle but this was the best compromise for
+ // now.
+ struct sockaddr_in dest = {};
+ ASSERT_EQ(inet_pton(AF_INET, "8.8.8.8", &dest.sin_addr.s_addr), 1);
+ dest.sin_family = AF_INET;
+ dest.sin_port = kPort;
+ EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+
+ // Wait and make sure the socket receives the data.
+ struct pollfd pfd = {};
+ pfd.fd = socket_;
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(1));
+
+ // Now read and check that the packet is the one we just sent.
+ // Read and verify the data.
+ constexpr size_t packet_size =
+ sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
+ char buf[64];
+ struct sockaddr_ll src = {};
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(socket_, buf, sizeof(buf), 0,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(packet_size));
+
+ // sockaddr_ll ends with an 8 byte physical address field, but ethernet
+ // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
+ // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
+ // sizeof(sockaddr_ll).
+ ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
+
+ // Verify the source address.
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(src.sll_ifindex, ifindex);
+ EXPECT_EQ(src.sll_halen, ETH_ALEN);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ EXPECT_EQ(src.sll_pkttype, PACKET_OUTGOING);
+ // Verify the link address of the interface matches that of the non
+ // non loopback interface address we stored above.
+ for (int i = 0; i < src.sll_halen; i++) {
+ EXPECT_EQ(src.sll_addr[i], hwaddr[i]);
+ }
+
+ // Verify the IP header.
+ struct iphdr ip = {};
+ memcpy(&ip, buf, sizeof(ip));
+ EXPECT_EQ(ip.ihl, 5);
+ EXPECT_EQ(ip.version, 4);
+ EXPECT_EQ(ip.tot_len, htons(packet_size));
+ EXPECT_EQ(ip.protocol, IPPROTO_UDP);
+ EXPECT_EQ(ip.daddr, dest.sin_addr.s_addr);
+ EXPECT_NE(ip.saddr, htonl(INADDR_LOOPBACK));
+
+ // Verify the UDP header.
+ struct udphdr udp = {};
+ memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
+ EXPECT_EQ(udp.dest, kPort);
+ EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
+
+ // Verify the payload.
+ char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
+ EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
+}
+
+// Bind with invalid address.
+TEST_P(CookedPacketTest, BindFail) {
+ // Null address.
+ ASSERT_THAT(
+ bind(socket_, nullptr, sizeof(struct sockaddr)),
+ AnyOf(SyscallFailsWithErrno(EFAULT), SyscallFailsWithErrno(EINVAL)));
+
+ // Address of size 1.
+ uint8_t addr = 0;
+ ASSERT_THAT(
+ bind(socket_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+INSTANTIATE_TEST_SUITE_P(AllInetTests, CookedPacketTest,
+ ::testing::Values(ETH_P_IP, ETH_P_ALL));
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc
index 07beb8ba0..aa7182b01 100644
--- a/test/syscalls/linux/packet_socket_raw.cc
+++ b/test/syscalls/linux/packet_socket_raw.cc
@@ -13,16 +13,13 @@
// limitations under the License.
#include <arpa/inet.h>
-#include <linux/capability.h>
-#include <linux/filter.h>
-#include <linux/if_arp.h>
-#include <linux/if_packet.h>
#include <net/ethernet.h>
+#include <net/if_arp.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
+#include <netpacket/packet.h>
#include <poll.h>
-#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
@@ -30,8 +27,10 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/endian.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/capability_util.h"
+#include "test/util/cleanup.h"
#include "test/util/file_descriptor.h"
#include "test/util/socket_util.h"
#include "test/util/test_util.h"
@@ -100,7 +99,7 @@ class RawPacketTest : public ::testing::TestWithParam<int> {
};
void RawPacketTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
ASSERT_THAT(socket(AF_PACKET, SOCK_RAW, htons(GetParam())),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -150,17 +149,15 @@ void RawPacketTest::SetUp() {
void RawPacketTest::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
EXPECT_THAT(close(s_), SyscallSucceeds());
}
}
int RawPacketTest::GetLoopbackIndex() {
- struct ifreq ifr;
- snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
- EXPECT_THAT(ioctl(s_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
- return ifr.ifr_ifindex;
+ int v = EXPECT_NO_ERRNO_AND_VALUE(gvisor::testing::GetLoopbackIndex());
+ EXPECT_NE(v, 0);
+ return v;
}
// Receive via a packet socket.
@@ -296,14 +293,6 @@ TEST_P(RawPacketTest, Send) {
memcpy(send_buf + sizeof(ethhdr) + sizeof(iphdr) + sizeof(udphdr), kMessage,
sizeof(kMessage));
- // We don't implement writing to packet sockets on gVisor.
- if (IsRunningOnGvisor()) {
- ASSERT_THAT(sendto(s_, send_buf, sizeof(send_buf), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallFailsWithErrno(EINVAL));
- GTEST_SKIP();
- }
-
// Send it.
ASSERT_THAT(sendto(s_, send_buf, sizeof(send_buf), 0,
reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
@@ -340,7 +329,7 @@ TEST_P(RawPacketTest, Send) {
// Check that setting SO_RCVBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawPacketTest, SetSocketRecvBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover minimum receive buf size by trying to set it to zero.
// See:
@@ -373,7 +362,7 @@ TEST_P(RawPacketTest, SetSocketRecvBufBelowMin) {
// Check that setting SO_RCVBUF above max is clamped to the maximum
// receive buffer size.
TEST_P(RawPacketTest, SetSocketRecvBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover max buf size by trying to set the largest possible buffer size.
constexpr int kRcvBufSz = 0xffffffff;
@@ -400,7 +389,7 @@ TEST_P(RawPacketTest, SetSocketRecvBufAboveMax) {
// Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored.
TEST_P(RawPacketTest, SetSocketRecvBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int max = 0;
int min = 0;
@@ -449,7 +438,7 @@ TEST_P(RawPacketTest, SetSocketRecvBuf) {
// Check that setting SO_SNDBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawPacketTest, SetSocketSendBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover minimum buffer size by trying to set it to zero.
constexpr int kSndBufSz = 0;
@@ -480,7 +469,7 @@ TEST_P(RawPacketTest, SetSocketSendBufBelowMin) {
// Check that setting SO_SNDBUF above max is clamped to the maximum
// send buffer size.
TEST_P(RawPacketTest, SetSocketSendBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover maximum buffer size by trying to set it to a large value.
constexpr int kSndBufSz = 0xffffffff;
@@ -507,7 +496,7 @@ TEST_P(RawPacketTest, SetSocketSendBufAboveMax) {
// Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored.
TEST_P(RawPacketTest, SetSocketSendBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int max = 0;
int min = 0;
@@ -551,7 +540,7 @@ TEST_P(RawPacketTest, SetSocketSendBuf) {
}
TEST_P(RawPacketTest, GetSocketError) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int val = 0;
socklen_t val_len = sizeof(val);
@@ -561,7 +550,7 @@ TEST_P(RawPacketTest, GetSocketError) {
}
TEST_P(RawPacketTest, GetSocketErrorBind) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
{
// Bind to the loopback device.
@@ -627,7 +616,7 @@ TEST_P(RawPacketTest, SetSocketDetachFilterNoInstalledFilter) {
}
TEST_P(RawPacketTest, GetSocketDetachFilter) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int val = 0;
socklen_t val_len = sizeof(val);
@@ -636,7 +625,7 @@ TEST_P(RawPacketTest, GetSocketDetachFilter) {
}
TEST_P(RawPacketTest, SetAndGetSocketLinger) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int level = SOL_SOCKET;
int type = SO_LINGER;
@@ -657,7 +646,7 @@ TEST_P(RawPacketTest, SetAndGetSocketLinger) {
}
TEST_P(RawPacketTest, GetSocketAcceptConn) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int got = -1;
socklen_t length = sizeof(got);
@@ -673,7 +662,7 @@ INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest,
class RawPacketMsgSizeTest : public ::testing::TestWithParam<TestAddress> {};
TEST_P(RawPacketMsgSizeTest, SendTooLong) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
TestAddress addr = GetParam().WithPort(kPort);
@@ -690,8 +679,11 @@ TEST_P(RawPacketMsgSizeTest, SendTooLong) {
SyscallFailsWithErrno(EMSGSIZE));
}
+// TODO(https://fxbug.dev/76957): Run this test on Fuchsia once splice is
+// available.
+#ifndef __Fuchsia__
TEST_P(RawPacketMsgSizeTest, SpliceTooLong) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
const char buf[65536] = {};
int fds[2];
@@ -718,6 +710,7 @@ TEST_P(RawPacketMsgSizeTest, SpliceTooLong) {
EXPECT_THAT(n, SyscallSucceedsWithValue(sizeof(buf)));
}
}
+#endif // __Fuchsia__
INSTANTIATE_TEST_SUITE_P(AllRawPacketMsgSizeTest, RawPacketMsgSizeTest,
::testing::Values(V4Loopback(), V6Loopback()));
diff --git a/test/syscalls/linux/ping_socket.cc b/test/syscalls/linux/ping_socket.cc
index 7ec1938bf..025c9568f 100644
--- a/test/syscalls/linux/ping_socket.cc
+++ b/test/syscalls/linux/ping_socket.cc
@@ -128,9 +128,6 @@ std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
{
.bind_to = V4Broadcast(),
.want = EADDRNOTAVAIL,
- // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
- // sockets are no longer allowed to bind to broadcast addresses.
- .want_gvisor = 0,
},
{
.bind_to = V4Loopback(),
@@ -139,9 +136,6 @@ std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
{
.bind_to = V4LoopbackSubnetBroadcast(),
.want = EADDRNOTAVAIL,
- // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
- // sockets are no longer allowed to bind to broadcast addresses.
- .want_gvisor = 0,
},
{
.bind_to = V4Multicast(),
@@ -155,43 +149,33 @@ std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
.bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"),
.want = EADDRNOTAVAIL,
},
- // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the test
- // cases below once ICMP sockets return EAFNOSUPPORT when binding to
- // IPv6 addresses.
{
.bind_to = V6Any(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6Loopback(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6Multicast(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6MulticastInterfaceLocalAllNodes(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6MulticastLinkLocalAllNodes(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6MulticastLinkLocalAllRouters(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
});
}
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc
index 0bba86846..209801e36 100644
--- a/test/syscalls/linux/pipe.cc
+++ b/test/syscalls/linux/pipe.cc
@@ -13,11 +13,13 @@
// limitations under the License.
#include <fcntl.h> /* Obtain O_* constant definitions */
+#include <linux/futex.h>
#include <linux/magic.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/statfs.h>
#include <sys/uio.h>
+#include <syscall.h>
#include <unistd.h>
#include <vector>
@@ -50,6 +52,9 @@ std::atomic<int> global_num_signals_received = 0;
void SigRecordingHandler(int signum, siginfo_t* siginfo,
void* unused_ucontext) {
global_num_signals_received++;
+ ASSERT_THAT(syscall(SYS_futex, &global_num_signals_received,
+ FUTEX_WAKE | FUTEX_PRIVATE_FLAG, INT_MAX, 0, 0, 0),
+ SyscallSucceeds());
}
PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) {
@@ -61,11 +66,14 @@ PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) {
return ScopedSigaction(signum, handler);
}
-void WaitForSignalDelivery(absl::Duration timeout, int max_expected) {
- absl::Time wait_start = absl::Now();
- while (global_num_signals_received < max_expected &&
- absl::Now() - wait_start < timeout) {
- absl::SleepFor(absl::Milliseconds(10));
+void WaitForSignalDelivery(int expected) {
+ while (1) {
+ int v = global_num_signals_received;
+ if (v >= expected) {
+ break;
+ }
+ RetryEINTR(syscall)(SYS_futex, &global_num_signals_received,
+ FUTEX_WAIT | FUTEX_PRIVATE_FLAG, v, 0, 0, 0);
}
}
@@ -371,7 +379,7 @@ TEST_P(PipeTest, ReaderSideCloses) {
EXPECT_THAT(write(wfd_.get(), &buf, sizeof(buf)),
SyscallFailsWithErrno(EPIPE));
- WaitForSignalDelivery(absl::Seconds(1), 1);
+ WaitForSignalDelivery(1);
ASSERT_EQ(global_num_signals_received, 1);
}
@@ -411,7 +419,7 @@ TEST_P(PipeTest, BlockWriteClosed) {
notify.WaitForNotification();
ASSERT_THAT(close(rfd_.release()), SyscallSucceeds());
- WaitForSignalDelivery(absl::Seconds(1), 1);
+ WaitForSignalDelivery(1);
ASSERT_EQ(global_num_signals_received, 1);
t.Join();
@@ -443,7 +451,7 @@ TEST_P(PipeTest, BlockPartialWriteClosed) {
// Unblock the above.
ASSERT_THAT(close(rfd_.release()), SyscallSucceeds());
- WaitForSignalDelivery(absl::Seconds(1), 2);
+ WaitForSignalDelivery(2);
ASSERT_EQ(global_num_signals_received, 2);
t.Join();
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index 8a4025fed..cb443163c 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -17,7 +17,6 @@
#include <fcntl.h>
#include <limits.h>
#include <linux/magic.h>
-#include <linux/sem.h>
#include <sched.h>
#include <signal.h>
#include <stddef.h>
@@ -2453,54 +2452,6 @@ TEST(ProcFilesystems, Bug65172365) {
ASSERT_FALSE(proc_filesystems.empty());
}
-TEST(ProcFilesystems, PresenceOfShmMaxMniAll) {
- uint64_t shmmax = 0;
- uint64_t shmall = 0;
- uint64_t shmmni = 0;
- std::string proc_file;
- proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmax"));
- ASSERT_FALSE(proc_file.empty());
- ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmax));
- proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmall"));
- ASSERT_FALSE(proc_file.empty());
- ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmall));
- proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmni"));
- ASSERT_FALSE(proc_file.empty());
- ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmni));
-
- ASSERT_GT(shmmax, 0);
- ASSERT_GT(shmall, 0);
- ASSERT_GT(shmmni, 0);
- ASSERT_LE(shmall, shmmax);
-
- // These values should never be higher than this by default, for more
- // information see uapi/linux/shm.h
- ASSERT_LE(shmmax, ULONG_MAX - (1UL << 24));
- ASSERT_LE(shmall, ULONG_MAX - (1UL << 24));
-}
-
-TEST(ProcFilesystems, PresenceOfSem) {
- uint32_t semmsl = 0;
- uint32_t semmns = 0;
- uint32_t semopm = 0;
- uint32_t semmni = 0;
- std::string proc_file;
- proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/sem"));
- ASSERT_FALSE(proc_file.empty());
- std::vector<absl::string_view> sem_limits =
- absl::StrSplit(proc_file, absl::ByAnyChar("\t"), absl::SkipWhitespace());
- ASSERT_EQ(sem_limits.size(), 4);
- ASSERT_TRUE(absl::SimpleAtoi(sem_limits[0], &semmsl));
- ASSERT_TRUE(absl::SimpleAtoi(sem_limits[1], &semmns));
- ASSERT_TRUE(absl::SimpleAtoi(sem_limits[2], &semopm));
- ASSERT_TRUE(absl::SimpleAtoi(sem_limits[3], &semmni));
-
- ASSERT_EQ(semmsl, SEMMSL);
- ASSERT_EQ(semmns, SEMMNS);
- ASSERT_EQ(semopm, SEMOPM);
- ASSERT_EQ(semmni, SEMMNI);
-}
-
// Check that /proc/mounts is a symlink to self/mounts.
TEST(ProcMounts, IsSymlink) {
auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/mounts"));
diff --git a/test/syscalls/linux/proc_isolated.cc b/test/syscalls/linux/proc_isolated.cc
new file mode 100644
index 000000000..38d079d2b
--- /dev/null
+++ b/test/syscalls/linux/proc_isolated.cc
@@ -0,0 +1,100 @@
+// Copyright 2021 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 <linux/msg.h>
+#include <linux/sem.h>
+#include <linux/shm.h>
+
+#include "gtest/gtest.h"
+#include "absl/strings/numbers.h"
+#include "absl/strings/str_split.h"
+#include "test/util/fs_util.h"
+#include "test/util/test_util.h"
+
+namespace gvisor {
+namespace testing {
+namespace {
+
+TEST(ProcDefaults, PresenceOfShmMaxMniAll) {
+ uint64_t shmmax = 0;
+ uint64_t shmall = 0;
+ uint64_t shmmni = 0;
+ std::string proc_file;
+ proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmax"));
+ ASSERT_FALSE(proc_file.empty());
+ ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmax));
+ proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmall"));
+ ASSERT_FALSE(proc_file.empty());
+ ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmall));
+ proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmni"));
+ ASSERT_FALSE(proc_file.empty());
+ ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmni));
+
+ ASSERT_EQ(shmmax, SHMMAX);
+ ASSERT_EQ(shmall, SHMALL);
+ ASSERT_EQ(shmmni, SHMMNI);
+ ASSERT_LE(shmall, shmmax);
+
+ // These values should never be higher than this by default, for more
+ // information see uapi/linux/shm.h
+ ASSERT_LE(shmmax, ULONG_MAX - (1UL << 24));
+ ASSERT_LE(shmall, ULONG_MAX - (1UL << 24));
+}
+
+TEST(ProcDefaults, PresenceOfSem) {
+ uint32_t semmsl = 0;
+ uint32_t semmns = 0;
+ uint32_t semopm = 0;
+ uint32_t semmni = 0;
+ std::string proc_file;
+ proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/sem"));
+ ASSERT_FALSE(proc_file.empty());
+ std::vector<absl::string_view> sem_limits =
+ absl::StrSplit(proc_file, absl::ByAnyChar("\t"), absl::SkipWhitespace());
+ ASSERT_EQ(sem_limits.size(), 4);
+ ASSERT_TRUE(absl::SimpleAtoi(sem_limits[0], &semmsl));
+ ASSERT_TRUE(absl::SimpleAtoi(sem_limits[1], &semmns));
+ ASSERT_TRUE(absl::SimpleAtoi(sem_limits[2], &semopm));
+ ASSERT_TRUE(absl::SimpleAtoi(sem_limits[3], &semmni));
+
+ ASSERT_EQ(semmsl, SEMMSL);
+ ASSERT_EQ(semmns, SEMMNS);
+ ASSERT_EQ(semopm, SEMOPM);
+ ASSERT_EQ(semmni, SEMMNI);
+}
+
+TEST(ProcDefaults, PresenceOfMsgMniMaxMnb) {
+ uint64_t msgmni = 0;
+ uint64_t msgmax = 0;
+ uint64_t msgmnb = 0;
+
+ std::string proc_file;
+ proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/msgmni"));
+ ASSERT_FALSE(proc_file.empty());
+ ASSERT_TRUE(absl::SimpleAtoi(proc_file, &msgmni));
+ proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/msgmax"));
+ ASSERT_FALSE(proc_file.empty());
+ ASSERT_TRUE(absl::SimpleAtoi(proc_file, &msgmax));
+ proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/msgmnb"));
+ ASSERT_FALSE(proc_file.empty());
+ ASSERT_TRUE(absl::SimpleAtoi(proc_file, &msgmnb));
+
+ ASSERT_EQ(msgmni, MSGMNI);
+ ASSERT_EQ(msgmax, MSGMAX);
+ ASSERT_EQ(msgmnb, MSGMNB);
+}
+
+} // namespace
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc
index 4cbe30fc1..672f2dd42 100644
--- a/test/syscalls/linux/proc_net.cc
+++ b/test/syscalls/linux/proc_net.cc
@@ -152,6 +152,37 @@ TEST(ProcNetDev, Format) {
EXPECT_GT(entries.size(), 0);
}
+// GetMibsAllocationSysctl retuns a value of the net.core.mibs_allocation
+// sysctl./proc/sys/net/core/mibs_allocation
+//
+// When mibs_allocation is unset, a netns creation inherits MIB from init
+// network namespace. Otherwise, MIBS is allocated for each namespace.
+int GetMibsAllocationSysctl() {
+ auto ret = GetContents("/proc/sys/net/core/mibs_allocation");
+ if (!ret.ok()) {
+ // The current kernel doesn't support mibs_allocation.
+ return 1;
+ }
+ int32_t val;
+ EXPECT_TRUE(absl::SimpleAtoi(ret.ValueOrDie(), &val));
+ return val;
+}
+
+// GetSNMPMetricFromProc retrieves the metric named `item` of `type` from the
+// `snmp` string which represents the file contents of /proc/net/snmp.
+//
+// Note to test writers: If you are writing tests to check the change in value
+// of SNMP metrics, note that if GetMibsAllocationSysctl() == 0
+// (net.core.mibs_allocation isn't set), MIB is from the init network namespace
+// and hence these metrics can have system-wide noise.
+//
+// A feasible way of testing is the following:
+// * Consult RFC 1213 to learn the "SYNTAX" of the metric.
+// * If net.core.mibs_allocation is set:
+// - Can test any metric while checking for hard equality.
+// * If net.core.mibs_allocation is NOT set (system-wide noise is present):
+// - Only test "SYNTAX Counter" metrics.
+// - Counter metrics are increasing in nature, so use LE/GE equality checks.
PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp,
const std::string& type,
const std::string& item) {
@@ -226,12 +257,25 @@ TEST(ProcNetSnmp, TcpReset) {
newAttemptFails = ASSERT_NO_ERRNO_AND_VALUE(
GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails"));
- EXPECT_EQ(oldActiveOpens, newActiveOpens - 1);
- EXPECT_EQ(oldOutRsts, newOutRsts - 1);
- EXPECT_EQ(oldAttemptFails, newAttemptFails - 1);
+ if (GetMibsAllocationSysctl()) {
+ EXPECT_EQ(oldActiveOpens + 1, newActiveOpens);
+ EXPECT_EQ(oldOutRsts + 1, newOutRsts);
+ EXPECT_EQ(oldAttemptFails + 1, newAttemptFails);
+ } else {
+ // System-wide statistics can have some noise. These metrics should have
+ // increased by at least 1.
+ EXPECT_LE(oldActiveOpens + 1, newActiveOpens);
+ EXPECT_LE(oldOutRsts + 1, newOutRsts);
+ EXPECT_LE(oldAttemptFails + 1, newAttemptFails);
+ }
}
TEST(ProcNetSnmp, TcpEstab) {
+ // This test aims to test the tcpCurrEstab metric which has "SYNTAX Gauge" as
+ // per RFC 1213. Hence, it becomes infeasible to test this when system-wide
+ // statistics have noise.
+ SKIP_IF(GetMibsAllocationSysctl() == 0);
+
// TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
DisableSave ds;
@@ -286,9 +330,9 @@ TEST(ProcNetSnmp, TcpEstab) {
newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
- EXPECT_EQ(oldActiveOpens, newActiveOpens - 1);
- EXPECT_EQ(oldPassiveOpens, newPassiveOpens - 1);
- EXPECT_EQ(oldCurrEstab, newCurrEstab - 2);
+ EXPECT_EQ(oldActiveOpens + 1, newActiveOpens);
+ EXPECT_EQ(oldPassiveOpens + 1, newPassiveOpens);
+ EXPECT_EQ(oldCurrEstab + 2, newCurrEstab);
// Send 1 byte from client to server.
ASSERT_THAT(send(s_connect.get(), "a", 1, 0), SyscallSucceedsWithValue(1));
@@ -355,8 +399,15 @@ TEST(ProcNetSnmp, UdpNoPorts) {
newNoPorts =
ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts"));
- EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1);
- EXPECT_EQ(oldNoPorts, newNoPorts - 1);
+ if (GetMibsAllocationSysctl()) {
+ EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_EQ(oldNoPorts + 1, newNoPorts);
+ } else {
+ // System-wide statistics can have some noise. These metrics should have
+ // increased by at least 1.
+ EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_LE(oldNoPorts + 1, newNoPorts);
+ }
}
TEST(ProcNetSnmp, UdpIn) {
@@ -405,8 +456,15 @@ TEST(ProcNetSnmp, UdpIn) {
newInDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams"));
- EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1);
- EXPECT_EQ(oldInDatagrams, newInDatagrams - 1);
+ if (GetMibsAllocationSysctl()) {
+ EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_EQ(oldInDatagrams + 1, newInDatagrams);
+ } else {
+ // System-wide statistics can have some noise. These metrics should have
+ // increased by at least 1.
+ EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_LE(oldInDatagrams + 1, newInDatagrams);
+ }
}
TEST(ProcNetSnmp, CheckNetStat) {
diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc
index 5ff1f12a0..c6592b4a7 100644
--- a/test/syscalls/linux/pty.cc
+++ b/test/syscalls/linux/pty.cc
@@ -1594,9 +1594,9 @@ TEST_F(JobControlTest, GetForegroundProcessGroupNonControlling) {
// - creates a child process in a new process group
// - sets that child as the foreground process group
// - kills its child and sets itself as the foreground process group.
-// TODO(gvisor.dev/issue/5357): Fix and enable.
-TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroup) {
+TEST_F(JobControlTest, SetForegroundProcessGroup) {
auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
// Ignore SIGTTOU so that we don't stop ourself when calling tcsetpgrp.
@@ -1634,12 +1634,90 @@ TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroup) {
// Set ourself as the foreground process.
pid_t pgid;
- TEST_PCHECK(pgid = getpgid(0) == 0);
+ TEST_PCHECK((pgid = getpgid(0)) >= 0);
TEST_PCHECK(!tcsetpgrp(replica_.get(), pgid));
});
ASSERT_NO_ERRNO(res);
}
+// This test verifies if a SIGTTOU signal is sent to the calling process's group
+// when tcsetpgrp is called by a background process
+TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUBackground) {
+ auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
+ TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
+ pid_t grandchild = fork();
+ if (!grandchild) {
+ // Assign a different pgid to the child so it will result as
+ // a background process.
+ TEST_PCHECK(!setpgid(grandchild, getpid()));
+ TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0)));
+ // We should never reach this.
+ _exit(1);
+ }
+ int wstatus;
+ TEST_PCHECK(waitpid(grandchild, &wstatus, WSTOPPED) == grandchild);
+ TEST_PCHECK(WSTOPSIG(wstatus) == SIGTTOU);
+ });
+ ASSERT_NO_ERRNO(res);
+}
+
+// This test verifies that a SIGTTOU signal is not delivered to
+// a background process which calls tcsetpgrp and is ignoring SIGTTOU
+TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUIgnored) {
+ auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
+ TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
+ pid_t grandchild = fork();
+ if (!grandchild) {
+ // Ignore SIGTTOU so the child in background won't
+ // be stopped when it will call tcsetpgrp
+ struct sigaction sa = {};
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGTTOU, &sa, NULL);
+ // Assign a different pgid to the child so it will result as
+ // a background process.
+ TEST_PCHECK(!setpgid(grandchild, getpid()));
+ TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0)));
+ _exit(0);
+ }
+ int wstatus;
+ TEST_PCHECK(waitpid(grandchild, &wstatus, WSTOPPED) == grandchild);
+ TEST_PCHECK(WSTOPSIG(wstatus) != SIGTTOU);
+ TEST_PCHECK(WIFEXITED(wstatus));
+ });
+ ASSERT_NO_ERRNO(res);
+}
+
+// This test verifies that a SIGTTOU signal is not delivered to
+// a background process which calls tcsetpgrp and is blocking SIGTTOU
+TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUBlocked) {
+ auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
+ TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
+ pid_t grandchild = fork();
+ if (!grandchild) {
+ // Block SIGTTOU so the child in background won't
+ // be stopped when it will call tcsetpgrp
+ sigset_t signal_set;
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, SIGTTOU);
+ sigprocmask(SIG_BLOCK, &signal_set, NULL);
+ // Assign a different pgid to the child so it will result as
+ // a background process.
+ TEST_PCHECK(!setpgid(grandchild, getpid()));
+ TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0)));
+ _exit(0);
+ }
+ int wstatus;
+ TEST_PCHECK(waitpid(grandchild, &wstatus, WSTOPPED) == grandchild);
+ TEST_PCHECK(WSTOPSIG(wstatus) != SIGTTOU);
+ });
+ ASSERT_NO_ERRNO(res);
+}
+
TEST_F(JobControlTest, SetForegroundProcessGroupWrongTTY) {
pid_t pid = getpid();
ASSERT_THAT(ioctl(replica_.get(), TIOCSPGRP, &pid),
@@ -1657,9 +1735,9 @@ TEST_F(JobControlTest, SetForegroundProcessGroupNegPgid) {
ASSERT_NO_ERRNO(ret);
}
-// TODO(gvisor.dev/issue/5357): Fix and enable.
-TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroupEmptyProcessGroup) {
+TEST_F(JobControlTest, SetForegroundProcessGroupEmptyProcessGroup) {
auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
// Create a new process, put it in a new process group, make that group the
diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc
index e19fe8f6b..ef1db47ee 100644
--- a/test/syscalls/linux/raw_socket.cc
+++ b/test/syscalls/linux/raw_socket.cc
@@ -13,8 +13,6 @@
// limitations under the License.
#include <arpa/inet.h>
-#include <linux/capability.h>
-#include <linux/filter.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
@@ -99,7 +97,7 @@ class RawSocketTest : public ::testing::TestWithParam<std::tuple<int, int>> {
};
void RawSocketTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
ASSERT_THAT(socket(Family(), SOCK_RAW, Protocol()),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -123,7 +121,7 @@ void RawSocketTest::SetUp() {
void RawSocketTest::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
EXPECT_THAT(close(s_), SyscallSucceeds());
}
}
@@ -132,7 +130,7 @@ void RawSocketTest::TearDown() {
// BasicRawSocket::Setup creates the first one, so we only have to create one
// more here.
TEST_P(RawSocketTest, MultipleCreation) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int s2;
ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds());
@@ -142,7 +140,7 @@ TEST_P(RawSocketTest, MultipleCreation) {
// Test that shutting down an unconnected socket fails.
TEST_P(RawSocketTest, FailShutdownWithoutConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(shutdown(s_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN));
ASSERT_THAT(shutdown(s_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN));
@@ -150,7 +148,7 @@ TEST_P(RawSocketTest, FailShutdownWithoutConnect) {
// Shutdown is a no-op for raw sockets (and datagram sockets in general).
TEST_P(RawSocketTest, ShutdownWriteNoop) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -165,7 +163,7 @@ TEST_P(RawSocketTest, ShutdownWriteNoop) {
// Shutdown is a no-op for raw sockets (and datagram sockets in general).
TEST_P(RawSocketTest, ShutdownReadNoop) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -182,14 +180,14 @@ TEST_P(RawSocketTest, ShutdownReadNoop) {
// Test that listen() fails.
TEST_P(RawSocketTest, FailListen) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(listen(s_, 1), SyscallFailsWithErrno(ENOTSUP));
}
// Test that accept() fails.
TEST_P(RawSocketTest, FailAccept) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr saddr;
socklen_t addrlen;
@@ -197,7 +195,7 @@ TEST_P(RawSocketTest, FailAccept) {
}
TEST_P(RawSocketTest, BindThenGetSockName) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_);
ASSERT_THAT(bind(s_, addr, AddrLen()), SyscallSucceeds());
@@ -221,7 +219,7 @@ TEST_P(RawSocketTest, BindThenGetSockName) {
}
TEST_P(RawSocketTest, ConnectThenGetSockName) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_);
ASSERT_THAT(connect(s_, addr, AddrLen()), SyscallSucceeds());
@@ -246,7 +244,7 @@ TEST_P(RawSocketTest, ConnectThenGetSockName) {
// Test that getpeername() returns nothing before connect().
TEST_P(RawSocketTest, FailGetPeerNameBeforeConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr saddr;
socklen_t addrlen = sizeof(saddr);
@@ -256,7 +254,7 @@ TEST_P(RawSocketTest, FailGetPeerNameBeforeConnect) {
// Test that getpeername() returns something after connect().
TEST_P(RawSocketTest, GetPeerName) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -270,7 +268,7 @@ TEST_P(RawSocketTest, GetPeerName) {
// Test that the socket is writable immediately.
TEST_P(RawSocketTest, PollWritableImmediately) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct pollfd pfd = {};
pfd.fd = s_;
@@ -280,7 +278,7 @@ TEST_P(RawSocketTest, PollWritableImmediately) {
// Test that the socket isn't readable before receiving anything.
TEST_P(RawSocketTest, PollNotReadableInitially) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Try to receive data with MSG_DONTWAIT, which returns immediately if there's
// nothing to be read.
@@ -291,7 +289,7 @@ TEST_P(RawSocketTest, PollNotReadableInitially) {
// Test that the socket becomes readable once something is written to it.
TEST_P(RawSocketTest, PollTriggeredOnWrite) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Write something so that there's data to be read.
// Arbitrary.
@@ -306,7 +304,7 @@ TEST_P(RawSocketTest, PollTriggeredOnWrite) {
// Test that we can connect() to a valid IP (loopback).
TEST_P(RawSocketTest, ConnectToLoopback) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -315,7 +313,7 @@ TEST_P(RawSocketTest, ConnectToLoopback) {
// Test that calling send() without connect() fails.
TEST_P(RawSocketTest, SendWithoutConnectFails) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Arbitrary.
constexpr char kBuf[] = "Endgame was good";
@@ -325,7 +323,7 @@ TEST_P(RawSocketTest, SendWithoutConnectFails) {
// Wildcard Bind.
TEST_P(RawSocketTest, BindToWildcard) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr_storage addr;
addr = {};
@@ -346,16 +344,15 @@ TEST_P(RawSocketTest, BindToWildcard) {
// Bind to localhost.
TEST_P(RawSocketTest, BindToLocalhost) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
}
// Bind to a different address.
TEST_P(RawSocketTest, BindToInvalid) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr_storage bind_addr = addr_;
if (Family() == AF_INET) {
@@ -367,13 +364,14 @@ TEST_P(RawSocketTest, BindToInvalid) {
memset(&sin6->sin6_addr.s6_addr, 0, sizeof(sin6->sin6_addr.s6_addr));
sin6->sin6_addr.s6_addr[0] = 1; // 1: - An address that we can't bind to.
}
- ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- AddrLen()), SyscallFailsWithErrno(EADDRNOTAVAIL));
+ ASSERT_THAT(
+ bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr), AddrLen()),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
}
// Send and receive an packet.
TEST_P(RawSocketTest, SendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Arbitrary.
constexpr char kBuf[] = "TB12";
@@ -388,7 +386,7 @@ TEST_P(RawSocketTest, SendAndReceive) {
// We should be able to create multiple raw sockets for the same protocol and
// receive the same packet on both.
TEST_P(RawSocketTest, MultipleSocketReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int s2;
ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds());
@@ -403,11 +401,11 @@ TEST_P(RawSocketTest, MultipleSocketReceive) {
// Receive it on socket 2.
std::vector<char> recv_buf2(sizeof(kBuf) + HdrLen());
- ASSERT_NO_FATAL_FAILURE(ReceiveBufFrom(s2, recv_buf2.data(),
- recv_buf2.size()));
+ ASSERT_NO_FATAL_FAILURE(
+ ReceiveBufFrom(s2, recv_buf2.data(), recv_buf2.size()));
- EXPECT_EQ(memcmp(recv_buf1.data() + HdrLen(),
- recv_buf2.data() + HdrLen(), sizeof(kBuf)),
+ EXPECT_EQ(memcmp(recv_buf1.data() + HdrLen(), recv_buf2.data() + HdrLen(),
+ sizeof(kBuf)),
0);
ASSERT_THAT(close(s2), SyscallSucceeds());
@@ -415,7 +413,7 @@ TEST_P(RawSocketTest, MultipleSocketReceive) {
// Test that connect sends packets to the right place.
TEST_P(RawSocketTest, SendAndReceiveViaConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -434,11 +432,10 @@ TEST_P(RawSocketTest, SendAndReceiveViaConnect) {
// Bind to localhost, then send and receive packets.
TEST_P(RawSocketTest, BindSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
// Arbitrary.
constexpr char kBuf[] = "DR16";
@@ -452,11 +449,10 @@ TEST_P(RawSocketTest, BindSendAndReceive) {
// Bind and connect to localhost and send/receive packets.
TEST_P(RawSocketTest, BindConnectSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
SyscallSucceeds());
@@ -474,7 +470,7 @@ TEST_P(RawSocketTest, BindConnectSendAndReceive) {
// Check that setting SO_RCVBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawSocketTest, SetSocketRecvBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover minimum receive buf size by trying to set it to zero.
// See:
@@ -507,7 +503,7 @@ TEST_P(RawSocketTest, SetSocketRecvBufBelowMin) {
// Check that setting SO_RCVBUF above max is clamped to the maximum
// receive buffer size.
TEST_P(RawSocketTest, SetSocketRecvBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover max buf size by trying to set the largest possible buffer size.
constexpr int kRcvBufSz = 0xffffffff;
@@ -534,7 +530,7 @@ TEST_P(RawSocketTest, SetSocketRecvBufAboveMax) {
// Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored.
TEST_P(RawSocketTest, SetSocketRecvBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int max = 0;
int min = 0;
@@ -584,7 +580,7 @@ TEST_P(RawSocketTest, SetSocketRecvBuf) {
// Check that setting SO_SNDBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawSocketTest, SetSocketSendBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover minimum buffer size by trying to set it to zero.
constexpr int kSndBufSz = 0;
@@ -615,7 +611,7 @@ TEST_P(RawSocketTest, SetSocketSendBufBelowMin) {
// Check that setting SO_SNDBUF above max is clamped to the maximum
// send buffer size.
TEST_P(RawSocketTest, SetSocketSendBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover maximum buffer size by trying to set it to a large value.
constexpr int kSndBufSz = 0xffffffff;
@@ -642,7 +638,7 @@ TEST_P(RawSocketTest, SetSocketSendBufAboveMax) {
// Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored.
TEST_P(RawSocketTest, SetSocketSendBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int max = 0;
int min = 0;
@@ -688,11 +684,10 @@ TEST_P(RawSocketTest, SetSocketSendBuf) {
// Test that receive buffer limits are not enforced when the recv buffer is
// empty.
TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
SyscallSucceeds());
@@ -719,9 +714,7 @@ TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) {
// Receive the packet and make sure it's identical.
std::vector<char> recv_buf(buf.size() + HdrLen());
ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size()));
- EXPECT_EQ(
- memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()),
- 0);
+ EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0);
}
{
@@ -734,9 +727,7 @@ TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) {
// Receive the packet and make sure it's identical.
std::vector<char> recv_buf(buf.size() + HdrLen());
ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size()));
- EXPECT_EQ(
- memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()),
- 0);
+ EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0);
}
}
@@ -750,11 +741,10 @@ TEST_P(RawSocketTest, RecvBufLimits) {
if (Protocol() == IPPROTO_TCP) {
return;
}
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
SyscallSucceeds());
@@ -814,9 +804,7 @@ TEST_P(RawSocketTest, RecvBufLimits) {
// Receive the packet and make sure it's identical.
std::vector<char> recv_buf(buf.size() + HdrLen());
ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size()));
- EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(),
- buf.size()),
- 0);
+ EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0);
}
// Assert that the last packet is dropped because the receive buffer should
@@ -885,7 +873,7 @@ TEST_P(RawSocketTest, GetSocketDetachFilter) {
// AF_INET6+SOCK_RAW+IPPROTO_RAW sockets can be created, but not written to.
TEST(RawSocketTest, IPv6ProtoRaw) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int sock;
ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW),
@@ -902,7 +890,7 @@ TEST(RawSocketTest, IPv6ProtoRaw) {
}
TEST(RawSocketTest, IPv6SendMsg) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int sock;
ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP),
@@ -930,7 +918,7 @@ TEST(RawSocketTest, IPv6SendMsg) {
}
TEST_P(RawSocketTest, ConnectOnIPv6Socket) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int sock;
ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP),
@@ -951,6 +939,124 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Combine(::testing::Values(IPPROTO_TCP, IPPROTO_UDP),
::testing::Values(AF_INET, AF_INET6)));
+void TestRawSocketMaybeBindReceive(bool do_bind) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
+
+ constexpr char payload[] = "abcdefgh";
+
+ const sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)},
+ };
+
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ sockaddr_in udp_sock_bind_addr = addr;
+ socklen_t udp_sock_bind_addr_len = sizeof(udp_sock_bind_addr);
+ ASSERT_THAT(bind(udp_sock.get(),
+ reinterpret_cast<const sockaddr*>(&udp_sock_bind_addr),
+ sizeof(udp_sock_bind_addr)),
+ SyscallSucceeds());
+ ASSERT_THAT(getsockname(udp_sock.get(),
+ reinterpret_cast<sockaddr*>(&udp_sock_bind_addr),
+ &udp_sock_bind_addr_len),
+ SyscallSucceeds());
+ ASSERT_EQ(udp_sock_bind_addr_len, sizeof(udp_sock_bind_addr));
+
+ FileDescriptor raw_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_UDP));
+
+ auto test_recv = [&](const char* scope, uint32_t expected_destination) {
+ SCOPED_TRACE(scope);
+
+ constexpr int kInfinitePollTimeout = -1;
+ pollfd pfd = {
+ .fd = raw_sock.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfinitePollTimeout),
+ SyscallSucceedsWithValue(1));
+
+ struct ipv4_udp_packet {
+ iphdr ip;
+ udphdr udp;
+ char data[sizeof(payload)];
+
+ // Used to make sure only the required space is used.
+ char unused_space;
+ } ABSL_ATTRIBUTE_PACKED;
+ constexpr size_t kExpectedIPPacketSize =
+ offsetof(ipv4_udp_packet, unused_space);
+
+ // Receive the whole IPv4 packet on the raw socket.
+ ipv4_udp_packet read_raw_packet;
+ sockaddr_in peer;
+ socklen_t peerlen = sizeof(peer);
+ ASSERT_EQ(
+ recvfrom(raw_sock.get(), reinterpret_cast<char*>(&read_raw_packet),
+ sizeof(read_raw_packet), 0 /* flags */,
+ reinterpret_cast<sockaddr*>(&peer), &peerlen),
+ static_cast<ssize_t>(kExpectedIPPacketSize))
+ << strerror(errno);
+ ASSERT_EQ(peerlen, sizeof(peer));
+ EXPECT_EQ(read_raw_packet.ip.version, static_cast<unsigned int>(IPVERSION));
+ // IHL holds the number of header bytes in 4 byte units.
+ EXPECT_EQ(read_raw_packet.ip.ihl, sizeof(read_raw_packet.ip) / 4);
+ EXPECT_EQ(ntohs(read_raw_packet.ip.tot_len), kExpectedIPPacketSize);
+ EXPECT_EQ(ntohs(read_raw_packet.ip.frag_off) & IP_OFFMASK, 0);
+ EXPECT_EQ(read_raw_packet.ip.protocol, SOL_UDP);
+ EXPECT_EQ(ntohl(read_raw_packet.ip.saddr), INADDR_LOOPBACK);
+ EXPECT_EQ(ntohl(read_raw_packet.ip.daddr), expected_destination);
+ EXPECT_EQ(read_raw_packet.udp.source, udp_sock_bind_addr.sin_port);
+ EXPECT_EQ(read_raw_packet.udp.dest, udp_sock_bind_addr.sin_port);
+ EXPECT_EQ(ntohs(read_raw_packet.udp.len),
+ kExpectedIPPacketSize - sizeof(read_raw_packet.ip));
+ for (size_t i = 0; i < sizeof(payload); i++) {
+ EXPECT_EQ(read_raw_packet.data[i], payload[i])
+ << "byte mismatch @ idx=" << i;
+ }
+ EXPECT_EQ(peer.sin_family, AF_INET);
+ EXPECT_EQ(peer.sin_port, 0);
+ EXPECT_EQ(ntohl(peer.sin_addr.s_addr), INADDR_LOOPBACK);
+ };
+
+ if (do_bind) {
+ ASSERT_THAT(bind(raw_sock.get(), reinterpret_cast<const sockaddr*>(&addr),
+ sizeof(addr)),
+ SyscallSucceeds());
+ }
+
+ constexpr int kSendToFlags = 0;
+ sockaddr_in different_addr = udp_sock_bind_addr;
+ different_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK + 1);
+ ASSERT_THAT(sendto(udp_sock.get(), payload, sizeof(payload), kSendToFlags,
+ reinterpret_cast<const sockaddr*>(&different_addr),
+ sizeof(different_addr)),
+ SyscallSucceedsWithValue(sizeof(payload)));
+ if (!do_bind) {
+ ASSERT_NO_FATAL_FAILURE(
+ test_recv("different_addr", ntohl(different_addr.sin_addr.s_addr)));
+ }
+ ASSERT_THAT(sendto(udp_sock.get(), payload, sizeof(payload), kSendToFlags,
+ reinterpret_cast<const sockaddr*>(&udp_sock_bind_addr),
+ sizeof(udp_sock_bind_addr)),
+ SyscallSucceedsWithValue(sizeof(payload)));
+ ASSERT_NO_FATAL_FAILURE(
+ test_recv("addr", ntohl(udp_sock_bind_addr.sin_addr.s_addr)));
+}
+
+TEST(RawSocketTest, UnboundReceive) {
+ // Test that a raw socket receives packets destined to any address if it is
+ // not bound to an address.
+ ASSERT_NO_FATAL_FAILURE(TestRawSocketMaybeBindReceive(false /* do_bind */));
+}
+
+TEST(RawSocketTest, BindReceive) {
+ // Test that a raw socket only receives packets destined to the address it is
+ // bound to.
+ ASSERT_NO_FATAL_FAILURE(TestRawSocketMaybeBindReceive(true /* do_bind */));
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/raw_socket_hdrincl.cc b/test/syscalls/linux/raw_socket_hdrincl.cc
index f1d8fd295..d45bd07bc 100644
--- a/test/syscalls/linux/raw_socket_hdrincl.cc
+++ b/test/syscalls/linux/raw_socket_hdrincl.cc
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <linux/capability.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
@@ -63,7 +62,7 @@ class RawHDRINCL : public ::testing::Test {
};
void RawHDRINCL::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_RAW),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -81,7 +80,7 @@ void RawHDRINCL::SetUp() {
void RawHDRINCL::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
EXPECT_THAT(close(socket_), SyscallSucceeds());
}
}
diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc
index 27d3fffee..3f9717284 100644
--- a/test/syscalls/linux/raw_socket_icmp.cc
+++ b/test/syscalls/linux/raw_socket_icmp.cc
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <linux/capability.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
@@ -77,7 +76,7 @@ class RawSocketICMPTest : public ::testing::Test {
};
void RawSocketICMPTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_ICMP),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -95,7 +94,7 @@ void RawSocketICMPTest::SetUp() {
void RawSocketICMPTest::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
EXPECT_THAT(close(s_), SyscallSucceeds());
}
}
@@ -103,7 +102,7 @@ void RawSocketICMPTest::TearDown() {
// We'll only read an echo in this case, as the kernel won't respond to the
// malformed ICMP checksum.
TEST_F(RawSocketICMPTest, SendAndReceiveBadChecksum) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence,
// and ID. None of that should matter for raw sockets - the kernel should
@@ -132,7 +131,7 @@ TEST_F(RawSocketICMPTest, SendAndReceiveBadChecksum) {
// Send and receive an ICMP packet.
TEST_F(RawSocketICMPTest, SendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Prepare and send an ICMP packet. Use arbitrary junk for sequence and ID.
// None of that should matter for raw sockets - the kernel should still give
@@ -152,7 +151,7 @@ TEST_F(RawSocketICMPTest, SendAndReceive) {
// We should be able to create multiple raw sockets for the same protocol and
// receive the same packet on both.
TEST_F(RawSocketICMPTest, MultipleSocketReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor s2 =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP));
@@ -215,7 +214,7 @@ TEST_F(RawSocketICMPTest, MultipleSocketReceive) {
// A raw ICMP socket and ping socket should both receive the ICMP packets
// intended for the ping socket.
TEST_F(RawSocketICMPTest, RawAndPingSockets) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor ping_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
@@ -265,7 +264,7 @@ TEST_F(RawSocketICMPTest, RawAndPingSockets) {
// while a ping socket should not. Neither should be able to receieve a short
// malformed packet.
TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor ping_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
@@ -306,7 +305,7 @@ TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) {
// while ping socket should not.
// Neither should be able to receieve a short malformed packet.
TEST_F(RawSocketICMPTest, ShortEchoReplyRawAndPingSockets) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor ping_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
@@ -345,7 +344,7 @@ TEST_F(RawSocketICMPTest, ShortEchoReplyRawAndPingSockets) {
// Test that connect() sends packets to the right place.
TEST_F(RawSocketICMPTest, SendAndReceiveViaConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
@@ -369,7 +368,7 @@ TEST_F(RawSocketICMPTest, SendAndReceiveViaConnect) {
// Bind to localhost, then send and receive packets.
TEST_F(RawSocketICMPTest, BindSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
@@ -392,7 +391,7 @@ TEST_F(RawSocketICMPTest, BindSendAndReceive) {
// Bind and connect to localhost and send/receive packets.
TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
@@ -418,7 +417,7 @@ TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) {
// Set and get SO_LINGER.
TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int level = SOL_SOCKET;
int type = SO_LINGER;
@@ -440,7 +439,7 @@ TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) {
// Test getsockopt for SO_ACCEPTCONN.
TEST_F(RawSocketICMPTest, GetSocketAcceptConn) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int got = -1;
socklen_t length = sizeof(got);
diff --git a/test/syscalls/linux/sendfile.cc b/test/syscalls/linux/sendfile.cc
index bea4ee71c..9bd3bd5e8 100644
--- a/test/syscalls/linux/sendfile.cc
+++ b/test/syscalls/linux/sendfile.cc
@@ -208,38 +208,6 @@ TEST(SendFileTest, SendAndUpdateFileOffset) {
absl::string_view(actual, kHalfDataSize));
}
-TEST(SendFileTest, SendToDevZeroAndUpdateFileOffset) {
- // Create temp files.
- // Test input string length must be > 2 AND even.
- constexpr char kData[] = "The slings and arrows of outrageous fortune,";
- constexpr int kDataSize = sizeof(kData) - 1;
- constexpr int kHalfDataSize = kDataSize / 2;
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
-
- // Open the input file as read only.
- const FileDescriptor inf =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
-
- // Open /dev/zero as write only.
- const FileDescriptor outf =
- ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY));
-
- // Send data and verify that sendfile returns the correct value.
- int bytes_sent;
- EXPECT_THAT(
- bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize),
- SyscallSucceedsWithValue(kHalfDataSize));
-
- char actual[kHalfDataSize];
- // Verify that the input file offset has been updated.
- ASSERT_THAT(read(inf.get(), &actual, kDataSize - bytes_sent),
- SyscallSucceedsWithValue(kHalfDataSize));
- EXPECT_EQ(
- absl::string_view(kData + kDataSize - bytes_sent, kDataSize - bytes_sent),
- absl::string_view(actual, kHalfDataSize));
-}
-
TEST(SendFileTest, SendAndUpdateFileOffsetFromNonzeroStartingPoint) {
// Create temp files.
// Test input string length must be > 2 AND divisible by 4.
@@ -609,23 +577,6 @@ TEST(SendFileTest, SendPipeBlocks) {
SyscallSucceedsWithValue(kDataSize));
}
-TEST(SendFileTest, SendToSpecialFile) {
- // Create temp file.
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode));
-
- const FileDescriptor inf =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
- constexpr int kSize = 0x7ff;
- ASSERT_THAT(ftruncate(inf.get(), kSize), SyscallSucceeds());
-
- auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
-
- // eventfd can accept a number of bytes which is a multiple of 8.
- EXPECT_THAT(sendfile(eventfd.get(), inf.get(), nullptr, 0xfffff),
- SyscallSucceedsWithValue(kSize & (~7)));
-}
-
TEST(SendFileTest, SendFileToPipe) {
// Create temp file.
constexpr char kData[] = "<insert-quote-here>";
@@ -672,57 +623,6 @@ TEST(SendFileTest, SendFileToSelf) {
SyscallSucceedsWithValue(kSendfileSize));
}
-static volatile int signaled = 0;
-void SigUsr1Handler(int sig, siginfo_t* info, void* context) { signaled = 1; }
-
-TEST(SendFileTest, ToEventFDDoesNotSpin) {
- FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0));
-
- // Write the maximum value of an eventfd to a file.
- const uint64_t kMaxEventfdValue = 0xfffffffffffffffe;
- const auto tempfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const auto tempfd = ASSERT_NO_ERRNO_AND_VALUE(Open(tempfile.path(), O_RDWR));
- ASSERT_THAT(
- pwrite(tempfd.get(), &kMaxEventfdValue, sizeof(kMaxEventfdValue), 0),
- SyscallSucceedsWithValue(sizeof(kMaxEventfdValue)));
-
- // Set the eventfd's value to 1.
- const uint64_t kOne = 1;
- ASSERT_THAT(write(efd.get(), &kOne, sizeof(kOne)),
- SyscallSucceedsWithValue(sizeof(kOne)));
-
- // Set up signal handler.
- struct sigaction sa = {};
- sa.sa_sigaction = SigUsr1Handler;
- sa.sa_flags = SA_SIGINFO;
- const auto cleanup_sigact =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa));
-
- // Send SIGUSR1 to this thread in 1 second.
- struct sigevent sev = {};
- sev.sigev_notify = SIGEV_THREAD_ID;
- sev.sigev_signo = SIGUSR1;
- sev.sigev_notify_thread_id = gettid();
- auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev));
- struct itimerspec its = {};
- its.it_value = absl::ToTimespec(absl::Seconds(1));
- DisableSave ds; // Asserting an EINTR.
- ASSERT_NO_ERRNO(timer.Set(0, its));
-
- // Sendfile from tempfd to the eventfd. Since the eventfd is not already at
- // its maximum value, the eventfd is "ready for writing"; however, since the
- // eventfd's existing value plus the new value would exceed the maximum, the
- // write should internally fail with EWOULDBLOCK. In this case, sendfile()
- // should block instead of spinning, and eventually be interrupted by our
- // timer. See b/172075629.
- EXPECT_THAT(
- sendfile(efd.get(), tempfd.get(), nullptr, sizeof(kMaxEventfdValue)),
- SyscallFailsWithErrno(EINTR));
-
- // Signal should have been handled.
- EXPECT_EQ(signaled, 1);
-}
-
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc
deleted file mode 100644
index af2459a2f..000000000
--- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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.
-
-#include "test/syscalls/linux/socket_ip_udp_unbound_external_networking.h"
-
-#include "test/util/socket_util.h"
-#include "test/util/test_util.h"
-
-namespace gvisor {
-namespace testing {
-
-void IPUDPUnboundExternalNetworkingSocketTest::SetUp() {
- // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
- // IPv4 address on eth0.
- found_net_interfaces_ = false;
-
- // Get interface list.
- ASSERT_NO_ERRNO(if_helper_.Load());
- std::vector<std::string> if_names = if_helper_.InterfaceList(AF_INET);
- if (if_names.size() != 2) {
- return;
- }
-
- // Figure out which interface is where.
- std::string lo = if_names[0];
- std::string eth = if_names[1];
- if (lo != "lo") std::swap(lo, eth);
- if (lo != "lo") return;
-
- lo_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(lo));
- auto lo_if_addr = if_helper_.GetAddr(AF_INET, lo);
- if (lo_if_addr == nullptr) {
- return;
- }
- lo_if_addr_ = *reinterpret_cast<const sockaddr_in*>(lo_if_addr);
-
- eth_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(eth));
- auto eth_if_addr = if_helper_.GetAddr(AF_INET, eth);
- if (eth_if_addr == nullptr) {
- return;
- }
- eth_if_addr_ = *reinterpret_cast<const sockaddr_in*>(eth_if_addr);
-
- found_net_interfaces_ = true;
-}
-
-} // namespace testing
-} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h
index 2e8aab129..92c20eba9 100644
--- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h
+++ b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h
@@ -16,29 +16,13 @@
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
// Test fixture for tests that apply to unbound IP UDP sockets in a sandbox
// with external networking support.
-class IPUDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest {
- protected:
- void SetUp() override;
-
- IfAddrHelper if_helper_;
-
- // found_net_interfaces_ is set to false if SetUp() could not obtain
- // all interface infos that we need.
- bool found_net_interfaces_;
-
- // Interface infos.
- int lo_if_idx_;
- int eth_if_idx_;
- sockaddr_in lo_if_addr_;
- sockaddr_in eth_if_addr_;
-};
+class IPUDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest {};
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc
new file mode 100644
index 000000000..0e3e7057b
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc
@@ -0,0 +1,299 @@
+// 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 "test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h"
+
+#include "gtest/gtest.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/util/capability_util.h"
+
+namespace gvisor {
+namespace testing {
+
+void IPv4DatagramBasedUnboundSocketTest::SetUp() {
+ if (GetParam().type & SOCK_RAW) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
+ }
+}
+
+// Check that dropping a group membership that does not exist fails.
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastInvalidDrop) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ // Unregister from a membership that we didn't have.
+ ip_mreq group = {};
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group,
+ sizeof(group)),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfZero) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn iface = {};
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallSucceeds());
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfInvalidNic) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn iface = {};
+ iface.imr_ifindex = -1;
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfInvalidAddr) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreq iface = {};
+ iface.imr_interface.s_addr = inet_addr("255.255.255");
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetShort) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ // Create a valid full-sized request.
+ ip_mreqn iface = {};
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+
+ // Send an optlen of 1 to check that optlen is enforced.
+ EXPECT_THAT(
+ setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, 1),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfDefault) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfDefaultReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
+ // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
+ // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
+ EXPECT_EQ(size, sizeof(in_addr));
+
+ // getsockopt(IP_MULTICAST_IF) will only return the interface address which
+ // hasn't been set.
+ EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetAddrGetReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ in_addr set = {};
+ set.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
+ // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
+ // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
+ EXPECT_EQ(size, sizeof(in_addr));
+ EXPECT_EQ(get.imr_multiaddr.s_addr, set.s_addr);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetReqAddrGetReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreq set = {};
+ set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
+ // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
+ // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
+ EXPECT_EQ(size, sizeof(in_addr));
+ EXPECT_EQ(get.imr_multiaddr.s_addr, set.imr_interface.s_addr);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetNicGetReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn set = {};
+ set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+ EXPECT_EQ(size, sizeof(in_addr));
+ EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetAddr) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ in_addr set = {};
+ set.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, set.s_addr);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetReqAddr) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreq set = {};
+ set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, set.imr_interface.s_addr);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetNic) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn set = {};
+ set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, TestJoinGroupNoIf) {
+ // TODO(b/185517803): Fix for native test.
+ SKIP_IF(!IsRunningOnGvisor());
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn group = {};
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
+ sizeof(group)),
+ SyscallFailsWithErrno(ENODEV));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, TestJoinGroupInvalidIf) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn group = {};
+ group.imr_address.s_addr = inet_addr("255.255.255");
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
+ sizeof(group)),
+ SyscallFailsWithErrno(ENODEV));
+}
+
+// Check that multiple memberships are not allowed on the same socket.
+TEST_P(IPv4DatagramBasedUnboundSocketTest, TestMultipleJoinsOnSingleSocket) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto fd = socket1->get();
+ ip_mreqn group = {};
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+
+ EXPECT_THAT(
+ setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
+ SyscallSucceeds());
+
+ EXPECT_THAT(
+ setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h
new file mode 100644
index 000000000..7ab235cad
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h
@@ -0,0 +1,31 @@
+// 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.
+
+#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_DATAGRAM_BASED_SOCKET_UNBOUND_H_
+#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_DATAGRAM_BASED_SOCKET_UNBOUND_H_
+
+#include "test/util/socket_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// Test fixture for tests that apply to IPv4 datagram-based sockets.
+class IPv4DatagramBasedUnboundSocketTest : public SimpleSocketTest {
+ void SetUp() override;
+};
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_DATAGRAM_BASED_SOCKET_UNBOUND_H_
diff --git a/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc
new file mode 100644
index 000000000..4ab4ef261
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc
@@ -0,0 +1,28 @@
+// Copyright 2018 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 "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h"
+
+namespace gvisor {
+namespace testing {
+
+INSTANTIATE_TEST_SUITE_P(
+ IPv4Sockets, IPv4DatagramBasedUnboundSocketTest,
+ ::testing::ValuesIn(ApplyVecToVec<SocketKind>(
+ {IPv4UDPUnboundSocket, IPv4RawUDPUnboundSocket, ICMPUnboundSocket},
+ AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK}))));
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
index 816d1181c..05773a29c 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
@@ -15,8 +15,6 @@
#include "test/syscalls/linux/socket_ipv4_udp_unbound.h"
#include <arpa/inet.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
@@ -26,10 +24,6 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/save_util.h"
-#include "test/util/socket_util.h"
-#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
@@ -140,7 +134,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNicNoDefaultSendIf) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -238,7 +232,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNic) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -326,7 +320,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNic) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -346,7 +340,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNic) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -437,7 +431,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicConnect) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -457,7 +451,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicConnect) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -548,7 +542,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelf) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -568,7 +562,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelf) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -657,7 +651,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfConnect) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -677,7 +671,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfConnect) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -770,7 +764,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -794,7 +788,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -819,20 +813,6 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) {
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
-// Check that dropping a group membership that does not exist fails.
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastInvalidDrop) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- // Unregister from a membership that we didn't have.
- ip_mreq group = {};
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group,
- sizeof(group)),
- SyscallFailsWithErrno(EADDRNOTAVAIL));
-}
-
// Check that dropping a group membership prevents multicast packets from being
// delivered. Default send address configured by bind and group membership
// interface configured by address.
@@ -917,7 +897,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropNic) {
// Register and unregister to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -943,260 +923,6 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropNic) {
PosixErrorIs(EAGAIN, ::testing::_));
}
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfZero) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn iface = {};
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
- sizeof(iface)),
- SyscallSucceeds());
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfInvalidNic) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn iface = {};
- iface.imr_ifindex = -1;
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
- sizeof(iface)),
- SyscallFailsWithErrno(EADDRNOTAVAIL));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfInvalidAddr) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreq iface = {};
- iface.imr_interface.s_addr = inet_addr("255.255.255");
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
- sizeof(iface)),
- SyscallFailsWithErrno(EADDRNOTAVAIL));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetShort) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- // Create a valid full-sized request.
- ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
-
- // Send an optlen of 1 to check that optlen is enforced.
- EXPECT_THAT(
- setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, 1),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfDefault) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfDefaultReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
- // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
- // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
- EXPECT_EQ(size, sizeof(in_addr));
-
- // getsockopt(IP_MULTICAST_IF) will only return the interface address which
- // hasn't been set.
- EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetAddrGetReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- in_addr set = {};
- set.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
- // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
- // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
- EXPECT_EQ(size, sizeof(in_addr));
- EXPECT_EQ(get.imr_multiaddr.s_addr, set.s_addr);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetReqAddrGetReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreq set = {};
- set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
- // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
- // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
- EXPECT_EQ(size, sizeof(in_addr));
- EXPECT_EQ(get.imr_multiaddr.s_addr, set.imr_interface.s_addr);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetNicGetReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn set = {};
- set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
- EXPECT_EQ(size, sizeof(in_addr));
- EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetAddr) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- in_addr set = {};
- set.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, set.s_addr);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetReqAddr) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreq set = {};
- set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, set.imr_interface.s_addr);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetNic) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn set = {};
- set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, TestJoinGroupNoIf) {
- // TODO(b/185517803): Fix for native test.
- SKIP_IF(!IsRunningOnGvisor());
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn group = {};
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
- sizeof(group)),
- SyscallFailsWithErrno(ENODEV));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, TestJoinGroupInvalidIf) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn group = {};
- group.imr_address.s_addr = inet_addr("255.255.255");
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
- sizeof(group)),
- SyscallFailsWithErrno(ENODEV));
-}
-
-// Check that multiple memberships are not allowed on the same socket.
-TEST_P(IPv4UDPUnboundSocketTest, TestMultipleJoinsOnSingleSocket) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto fd = socket1->get();
- ip_mreqn group = {};
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
-
- EXPECT_THAT(
- setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
- SyscallSucceeds());
-
- EXPECT_THAT(
- setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
// Check that two sockets can join the same multicast group at the same time.
TEST_P(IPv4UDPUnboundSocketTest, TestTwoSocketsJoinSameMulticastGroup) {
auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -1204,7 +930,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestTwoSocketsJoinSameMulticastGroup) {
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -1419,7 +1145,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenJoinThenReceive) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -2110,16 +1836,11 @@ TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) {
EXPECT_EQ(cmsg->cmsg_level, level);
EXPECT_EQ(cmsg->cmsg_type, type);
- // Get loopback index.
- ifreq ifr = {};
- absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo");
- ASSERT_THAT(ioctl(sender->get(), SIOCGIFINDEX, &ifr), SyscallSucceeds());
- ASSERT_NE(ifr.ifr_ifindex, 0);
-
// Check the data
in_pktinfo received_pktinfo = {};
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo));
- EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex);
+ EXPECT_EQ(received_pktinfo.ipi_ifindex,
+ ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, htonl(INADDR_LOOPBACK));
EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, htonl(INADDR_LOOPBACK));
}
@@ -2439,7 +2160,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIPPacketInfo) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
@@ -2484,16 +2205,10 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIPPacketInfo) {
EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IP);
EXPECT_EQ(cmsg->cmsg_type, IP_PKTINFO);
- // Get loopback index.
- ifreq ifr = {};
- absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo");
- ASSERT_THAT(ioctl(receiver_socket->get(), SIOCGIFINDEX, &ifr),
- SyscallSucceeds());
- ASSERT_NE(ifr.ifr_ifindex, 0);
-
in_pktinfo received_pktinfo = {};
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo));
- EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex);
+ EXPECT_EQ(received_pktinfo.ipi_ifindex,
+ ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
if (IsRunningOnGvisor()) {
// This should actually be a unicast address assigned to the interface.
//
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
index c6e775b2a..6c67ec51e 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc
@@ -14,9 +14,62 @@
#include "test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h"
+#include <net/if.h>
+
+#include "absl/cleanup/cleanup.h"
+#include "test/util/socket_util.h"
+#include "test/util/test_util.h"
+
namespace gvisor {
namespace testing {
+void IPv4UDPUnboundExternalNetworkingSocketTest::SetUp() {
+#ifdef ANDROID
+ GTEST_SKIP() << "Android does not support getifaddrs in r22";
+#endif
+
+ ifaddrs* ifaddr;
+ ASSERT_THAT(getifaddrs(&ifaddr), SyscallSucceeds());
+ auto cleanup = absl::MakeCleanup([ifaddr] { freeifaddrs(ifaddr); });
+
+ for (const ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
+ ASSERT_NE(ifa->ifa_name, nullptr);
+ ASSERT_NE(ifa->ifa_addr, nullptr);
+
+ if (ifa->ifa_addr->sa_family != AF_INET) {
+ continue;
+ }
+
+ std::optional<std::pair<int, sockaddr_in>>& if_pair = *[this, ifa]() {
+ if (strcmp(ifa->ifa_name, "lo") == 0) {
+ return &lo_if_;
+ }
+ return &eth_if_;
+ }();
+
+ const int if_index =
+ ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex(ifa->ifa_name));
+
+ std::cout << " name=" << ifa->ifa_name
+ << " addr=" << GetAddrStr(ifa->ifa_addr) << " index=" << if_index
+ << " has_value=" << if_pair.has_value() << std::endl;
+
+ if (if_pair.has_value()) {
+ continue;
+ }
+
+ if_pair = std::make_pair(
+ if_index, *reinterpret_cast<const sockaddr_in*>(ifa->ifa_addr));
+ }
+
+ if (!(eth_if_.has_value() && lo_if_.has_value())) {
+ // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its
+ // IPv4 address on eth0.
+ GTEST_SKIP() << " eth_if_.has_value()=" << eth_if_.has_value()
+ << " lo_if_.has_value()=" << lo_if_.has_value();
+ }
+}
+
TestAddress V4EmptyAddress() {
TestAddress t("V4Empty");
t.addr.ss_family = AF_INET;
@@ -28,7 +81,6 @@ TestAddress V4EmptyAddress() {
// the destination port number.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastReceivedOnExpectedPort) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -101,8 +153,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// not a unicast address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastReceivedOnExpectedAddresses) {
- SKIP_IF(!found_net_interfaces_);
-
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -149,7 +199,7 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Bind the non-receiving socket to the unicast ethernet address.
auto norecv_addr = rcv1_addr;
reinterpret_cast<sockaddr_in*>(&norecv_addr.addr)->sin_addr =
- eth_if_addr_.sin_addr;
+ eth_if_addr().sin_addr;
ASSERT_THAT(
bind(norcv->get(), AsSockAddr(&norecv_addr.addr), norecv_addr.addr_len),
SyscallSucceedsWithValue(0));
@@ -184,7 +234,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// (UDPBroadcastSendRecvOnSocketBoundToAny).
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastSendRecvOnSocketBoundToBroadcast) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Enable SO_BROADCAST.
@@ -224,7 +273,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// (UDPBroadcastSendRecvOnSocketBoundToBroadcast).
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
UDPBroadcastSendRecvOnSocketBoundToAny) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Enable SO_BROADCAST.
@@ -261,7 +309,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Verifies that a UDP broadcast fails to send on a socket with SO_BROADCAST
// disabled.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendBroadcast) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Broadcast a test message without having enabled SO_BROADCAST on the sending
@@ -306,12 +353,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendUnicastOnUnbound) {
// set interface or group membership.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastSelfNoGroup) {
- // FIXME(b/125485338): A group membership is not required for external
- // multicast on gVisor.
- SKIP_IF(IsRunningOnGvisor());
-
- SKIP_IF(!found_net_interfaces_);
-
auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto bind_addr = V4Any();
@@ -345,7 +386,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Check that multicast packets will be delivered to the sending socket without
// setting an interface.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelf) {
- SKIP_IF(!found_net_interfaces_);
auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto bind_addr = V4Any();
@@ -388,7 +428,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelf) {
// set interface and IP_MULTICAST_LOOP disabled.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastSelfLoopOff) {
- SKIP_IF(!found_net_interfaces_);
auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto bind_addr = V4Any();
@@ -434,12 +473,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Check that multicast packets won't be delivered to another socket with no
// set interface or group membership.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) {
- // FIXME(b/125485338): A group membership is not required for external
- // multicast on gVisor.
- SKIP_IF(IsRunningOnGvisor());
-
- SKIP_IF(!found_net_interfaces_);
-
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -476,7 +509,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) {
// Check that multicast packets will be delivered to another socket without
// setting an interface.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticast) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -522,7 +554,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticast) {
// set interface and IP_MULTICAST_LOOP disabled on the sending socket.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastSenderNoLoop) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -572,8 +603,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// setting an interface and IP_MULTICAST_LOOP disabled on the receiving socket.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastReceiverNoLoop) {
- SKIP_IF(!found_net_interfaces_);
-
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -624,7 +653,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// and both will receive data on it when bound to the ANY address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastToTwoBoundToAny) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
std::unique_ptr<FileDescriptor> receivers[2] = {
ASSERT_NO_ERRNO_AND_VALUE(NewSocket()),
@@ -689,7 +717,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// and both will receive data on it when bound to the multicast address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastToTwoBoundToMulticastAddress) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
std::unique_ptr<FileDescriptor> receivers[2] = {
ASSERT_NO_ERRNO_AND_VALUE(NewSocket()),
@@ -757,7 +784,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// multicast address, both will receive data.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
TestSendMulticastToTwoBoundToAnyAndMulticastAddress) {
- SKIP_IF(!found_net_interfaces_);
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
std::unique_ptr<FileDescriptor> receivers[2] = {
ASSERT_NO_ERRNO_AND_VALUE(NewSocket()),
@@ -829,8 +855,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// is not a multicast address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
IpMulticastLoopbackFromAddr) {
- SKIP_IF(!found_net_interfaces_);
-
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -893,8 +917,6 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// interface, a multicast packet sent out uses the latter as its source address.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
IpMulticastLoopbackIfNicAndAddr) {
- SKIP_IF(!found_net_interfaces_);
-
// Create receiver, bind to ANY and join the multicast group.
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver_addr = V4Any();
@@ -906,11 +928,15 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
&receiver_addr_len),
SyscallSucceeds());
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
- int receiver_port =
+ const in_port_t receiver_port =
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
- ip_mreqn group = {};
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = lo_if_idx_;
+ const ip_mreqn group = {
+ .imr_multiaddr =
+ {
+ .s_addr = inet_addr(kMulticastAddress),
+ },
+ .imr_ifindex = lo_if_idx(),
+ };
ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -918,9 +944,10 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Set outgoing multicast interface config, with NIC and addr pointing to
// different interfaces.
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- ip_mreqn iface = {};
- iface.imr_ifindex = lo_if_idx_;
- iface.imr_address = eth_if_addr_.sin_addr;
+ const ip_mreqn iface = {
+ .imr_address = eth_if_addr().sin_addr,
+ .imr_ifindex = lo_if_idx(),
+ };
ASSERT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -928,67 +955,104 @@ TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
// Send a multicast packet.
auto sendto_addr = V4Multicast();
reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = receiver_port;
- char send_buf[4] = {};
+ char send_buf[4];
ASSERT_THAT(
RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0,
AsSockAddr(&sendto_addr.addr), sendto_addr.addr_len),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Receive a multicast packet.
- char recv_buf[sizeof(send_buf)] = {};
+ char recv_buf[sizeof(send_buf) + 1];
auto src_addr = V4EmptyAddress();
ASSERT_THAT(
RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0,
AsSockAddr(&src_addr.addr), &src_addr.addr_len),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
- ASSERT_EQ(sizeof(struct sockaddr_in), src_addr.addr_len);
- sockaddr_in* src_addr_in = reinterpret_cast<sockaddr_in*>(&src_addr.addr);
-
- // FIXME (b/137781162): When sending a multicast packet use the proper logic
- // to determine the packet's src-IP.
- SKIP_IF(IsRunningOnGvisor());
+ SyscallSucceedsWithValue(sizeof(send_buf)));
+ ASSERT_EQ(src_addr.addr_len, sizeof(struct sockaddr_in));
// Verify the received source address.
- EXPECT_EQ(eth_if_addr_.sin_addr.s_addr, src_addr_in->sin_addr.s_addr);
+ //
+ // TODO(https://gvisor.dev/issue/6686): gVisor is a strong host, preventing
+ // the packet from being sent from the loopback device using the ethernet
+ // device's address.
+ if (IsRunningOnGvisor()) {
+ EXPECT_EQ(GetAddrStr(AsSockAddr(&src_addr.addr)),
+ GetAddr4Str(&lo_if_addr().sin_addr));
+ } else {
+ EXPECT_EQ(GetAddrStr(AsSockAddr(&src_addr.addr)),
+ GetAddr4Str(&eth_if_addr().sin_addr));
+ }
}
// Check that when we are bound to one interface we can set IP_MULTICAST_IF to
// another interface.
TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest,
IpMulticastLoopbackBindToOneIfSetMcastIfToAnother) {
- SKIP_IF(!found_net_interfaces_);
-
- // FIXME (b/137790511): When bound to one interface it is not possible to set
- // IP_MULTICAST_IF to a different interface.
- SKIP_IF(IsRunningOnGvisor());
-
- // Create sender and bind to eth interface.
- auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- ASSERT_THAT(
- bind(sender->get(), AsSockAddr(&eth_if_addr_), sizeof(eth_if_addr_)),
- SyscallSucceeds());
-
// Run through all possible combinations of index and address for
// IP_MULTICAST_IF that selects the loopback interface.
- struct {
- int imr_ifindex;
- struct in_addr imr_address;
- } test_data[] = {
- {lo_if_idx_, {}},
- {0, lo_if_addr_.sin_addr},
- {lo_if_idx_, lo_if_addr_.sin_addr},
- {lo_if_idx_, eth_if_addr_.sin_addr},
+ ip_mreqn ifaces[] = {
+ {
+ .imr_address = {},
+ .imr_ifindex = lo_if_idx(),
+ },
+ {
+ .imr_address = lo_if_addr().sin_addr,
+ .imr_ifindex = 0,
+ },
+ {
+ .imr_address = lo_if_addr().sin_addr,
+ .imr_ifindex = lo_if_idx(),
+ },
+ {
+ .imr_address = eth_if_addr().sin_addr,
+ .imr_ifindex = lo_if_idx(),
+ },
};
- for (auto t : test_data) {
- ip_mreqn iface = {};
- iface.imr_ifindex = t.imr_ifindex;
- iface.imr_address = t.imr_address;
- EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
- sizeof(iface)),
- SyscallSucceeds())
- << "imr_index=" << iface.imr_ifindex
- << " imr_address=" << GetAddr4Str(&iface.imr_address);
+
+ {
+ auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ ASSERT_THAT(
+ bind(sender->get(), AsSockAddr(&eth_if_addr()), sizeof(eth_if_addr())),
+ SyscallSucceeds());
+
+ for (const ip_mreqn& iface : ifaces) {
+ EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallSucceeds())
+ << " imr_index=" << iface.imr_ifindex
+ << " imr_address=" << GetAddr4Str(&iface.imr_address);
+ }
+ }
+
+ {
+ char eth_if_name[IF_NAMESIZE];
+ memset(eth_if_name, 0xAA, sizeof(eth_if_name));
+ ASSERT_NE(if_indextoname(eth_if_idx(), eth_if_name), nullptr)
+ << strerror(errno);
+ auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BINDTODEVICE,
+ eth_if_name, sizeof(eth_if_name)),
+ SyscallSucceeds());
+
+ for (const ip_mreqn& iface : ifaces) {
+ // FIXME(b/137790511): Disallow mismatching IP_MULTICAST_IF and
+ // SO_BINDTODEVICE.
+ if (IsRunningOnGvisor()) {
+ EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF,
+ &iface, sizeof(iface)),
+ SyscallSucceeds())
+ << " imr_index=" << iface.imr_ifindex
+ << " imr_address=" << GetAddr4Str(&iface.imr_address);
+ } else {
+ EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF,
+ &iface, sizeof(iface)),
+ SyscallFailsWithErrno(EINVAL))
+ << " imr_index=" << iface.imr_ifindex
+ << " imr_address=" << GetAddr4Str(&iface.imr_address);
+ }
+ }
}
}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
index 20922ac1f..ac917b32c 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h
@@ -22,8 +22,22 @@ namespace testing {
// Test fixture for tests that apply to unbound IPv4 UDP sockets in a sandbox
// with external networking support.
-using IPv4UDPUnboundExternalNetworkingSocketTest =
- IPUDPUnboundExternalNetworkingSocketTest;
+class IPv4UDPUnboundExternalNetworkingSocketTest
+ : public IPUDPUnboundExternalNetworkingSocketTest {
+ protected:
+ void SetUp() override;
+
+ int lo_if_idx() const { return std::get<0>(lo_if_.value()); }
+ int eth_if_idx() const { return std::get<0>(eth_if_.value()); }
+
+ const sockaddr_in& lo_if_addr() const { return std::get<1>(lo_if_.value()); }
+ const sockaddr_in& eth_if_addr() const {
+ return std::get<1>(eth_if_.value());
+ }
+
+ private:
+ std::optional<std::pair<int, sockaddr_in>> lo_if_, eth_if_;
+};
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc
index 00930c544..bef284530 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc
@@ -12,12 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <vector>
-
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_ipv4_udp_unbound.h"
-#include "test/util/socket_util.h"
-#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound.cc b/test/syscalls/linux/socket_ipv6_udp_unbound.cc
index 612fd531c..c431bf771 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound.cc
@@ -39,6 +39,86 @@
namespace gvisor {
namespace testing {
+using ::testing::IsNull;
+using ::testing::NotNull;
+
+TEST_P(IPv6UDPUnboundSocketTest, IPv6PacketInfo) {
+ auto sender_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto receiver_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ auto sender_addr = V6Loopback();
+ ASSERT_THAT(bind(sender_socket->get(), AsSockAddr(&sender_addr.addr),
+ sender_addr.addr_len),
+ SyscallSucceeds());
+
+ auto receiver_addr = V6Loopback();
+ ASSERT_THAT(bind(receiver_socket->get(), AsSockAddr(&receiver_addr.addr),
+ receiver_addr.addr_len),
+ SyscallSucceeds());
+ socklen_t receiver_addr_len = receiver_addr.addr_len;
+ ASSERT_THAT(getsockname(receiver_socket->get(),
+ AsSockAddr(&receiver_addr.addr), &receiver_addr_len),
+ SyscallSucceeds());
+ ASSERT_EQ(receiver_addr_len, receiver_addr.addr_len);
+
+ // Make sure we get IPv6 packet information as control messages.
+ constexpr int one = 1;
+ ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &one, sizeof(one)),
+ SyscallSucceeds());
+
+ // Send a packet - we don't care about the packet itself, only the returned
+ // IPV6_PKTINFO control message.
+ char send_buf[200];
+ RandomizeBuffer(send_buf, sizeof(send_buf));
+ ASSERT_THAT(RetryEINTR(sendto)(
+ sender_socket->get(), send_buf, sizeof(send_buf), 0,
+ AsSockAddr(&receiver_addr.addr), receiver_addr.addr_len),
+ SyscallSucceedsWithValue(sizeof(send_buf)));
+
+ // Check that we received the packet with the packet information control
+ // message.
+ char recv_buf[sizeof(send_buf) + 1];
+ in6_pktinfo received_pktinfo;
+ char recv_cmsg_buf[CMSG_SPACE(sizeof(received_pktinfo))];
+ iovec recv_iov = {
+ .iov_base = recv_buf,
+ .iov_len = sizeof(recv_buf),
+ };
+ msghdr recv_msg = {
+ .msg_iov = &recv_iov,
+ .msg_iovlen = 1,
+ .msg_control = recv_cmsg_buf,
+ .msg_controllen = sizeof(recv_cmsg_buf),
+ };
+ ASSERT_THAT(
+ RetryEINTR(recvmsg)(receiver_socket->get(), &recv_msg, 0 /* flags */),
+ SyscallSucceedsWithValue(sizeof(send_buf)));
+ EXPECT_EQ(memcmp(send_buf, recv_buf, sizeof(send_buf)), 0);
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&recv_msg);
+ ASSERT_THAT(cmsg, NotNull());
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(in6_pktinfo)));
+ EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IPV6);
+ EXPECT_EQ(cmsg->cmsg_type, IPV6_PKTINFO);
+ // As per cmsg(3) (https://www.man7.org/linux/man-pages/man3/cmsg.3.html),
+ //
+ // CMSG_DATA() returns a pointer to the data portion of a cmsghdr. The
+ // pointer returned cannot be assumed to be suitably aligned for accessing
+ // arbitrary payload data types. Applications should not cast it to a
+ // pointer type matching the payload, but should instead use memcpy(3) to
+ // copy data to or from a suitably declared object.
+ memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(received_pktinfo));
+ EXPECT_EQ(
+ memcmp(&received_pktinfo.ipi6_addr,
+ &(reinterpret_cast<sockaddr_in6*>(&sender_addr.addr)->sin6_addr),
+ sizeof(received_pktinfo.ipi6_addr)),
+ 0);
+ EXPECT_EQ(received_pktinfo.ipi6_ifindex,
+ ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
+ EXPECT_THAT(CMSG_NXTHDR(&recv_msg, cmsg), IsNull());
+}
+
// Test that socket will receive IP_RECVORIGDSTADDR control message.
TEST_P(IPv6UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) {
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc
index 09f070797..c6e563cd3 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc
@@ -18,8 +18,6 @@ namespace gvisor {
namespace testing {
TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestJoinLeaveMulticast) {
- SKIP_IF(!found_net_interfaces_);
-
auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -39,7 +37,7 @@ TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestJoinLeaveMulticast) {
.ipv6mr_multiaddr =
reinterpret_cast<sockaddr_in6*>(&multicast_addr.addr)->sin6_addr,
.ipv6mr_interface = static_cast<decltype(ipv6_mreq::ipv6mr_interface)>(
- ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"))),
+ ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex())),
};
ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
&group_req, sizeof(group_req)),
diff --git a/test/syscalls/linux/socket_unix.cc b/test/syscalls/linux/socket_unix.cc
index cf96b2075..43433eaae 100644
--- a/test/syscalls/linux/socket_unix.cc
+++ b/test/syscalls/linux/socket_unix.cc
@@ -27,7 +27,10 @@
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/file_descriptor.h"
+#include "test/util/memory_util.h"
#include "test/util/socket_util.h"
+#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -268,6 +271,18 @@ TEST_P(UnixSocketPairTest, SocketReopenFromProcfs) {
}
}
+// Repro for b/196804997.
+TEST_P(UnixSocketPairTest, SendFromMmapBeyondEof) {
+ TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
+ Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
+ Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, fd.get(), 0));
+
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ ASSERT_THAT(send(sockets->first_fd(), m.ptr(), m.len(), 0),
+ SyscallFailsWithErrno(EFAULT));
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/socket_unix_stream.cc b/test/syscalls/linux/socket_unix_stream.cc
index 6e9f70f8c..2f3cfc3f3 100644
--- a/test/syscalls/linux/socket_unix_stream.cc
+++ b/test/syscalls/linux/socket_unix_stream.cc
@@ -181,6 +181,21 @@ TEST_P(StreamUnixSocketPairTest, SetSocketSendBuf) {
ASSERT_EQ(quarter_sz, val);
}
+TEST_P(StreamUnixSocketPairTest, SendBufferOverflow) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ auto s = sockets->first_fd();
+
+ constexpr int kBufSz = 4096;
+ std::vector<char> buf(kBufSz * 4);
+ ASSERT_THAT(RetryEINTR(send)(s, buf.data(), buf.size(), MSG_DONTWAIT),
+ SyscallSucceeds());
+ // The new buffer size should be smaller that the amount of data in the queue.
+ ASSERT_THAT(setsockopt(s, SOL_SOCKET, SO_SNDBUF, &kBufSz, sizeof(kBufSz)),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(send)(s, buf.data(), buf.size(), MSG_DONTWAIT),
+ SyscallFailsWithErrno(EAGAIN));
+}
+
TEST_P(StreamUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int sock = sockets->first_fd();
diff --git a/test/syscalls/linux/splice.cc b/test/syscalls/linux/splice.cc
index c85f6da0b..4a10ae8d2 100644
--- a/test/syscalls/linux/splice.cc
+++ b/test/syscalls/linux/splice.cc
@@ -195,81 +195,6 @@ TEST(SpliceTest, PipeOffsets) {
SyscallFailsWithErrno(ESPIPE));
}
-// Event FDs may be used with splice without an offset.
-TEST(SpliceTest, FromEventFD) {
- // Open the input eventfd with an initial value so that it is readable.
- constexpr uint64_t kEventFDValue = 1;
- int efd;
- ASSERT_THAT(efd = eventfd(kEventFDValue, 0), SyscallSucceeds());
- const FileDescriptor in_fd(efd);
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Splice 8-byte eventfd value to pipe.
- constexpr int kEventFDSize = 8;
- EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, kEventFDSize, 0),
- SyscallSucceedsWithValue(kEventFDSize));
-
- // Contents should be equal.
- std::vector<char> rbuf(kEventFDSize);
- ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kEventFDSize));
- EXPECT_EQ(memcmp(rbuf.data(), &kEventFDValue, rbuf.size()), 0);
-}
-
-// Event FDs may not be used with splice with an offset.
-TEST(SpliceTest, FromEventFDOffset) {
- int efd;
- ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds());
- const FileDescriptor in_fd(efd);
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Attempt to splice 8-byte eventfd value to pipe with offset.
- //
- // This is not allowed because eventfd doesn't support pread.
- constexpr int kEventFDSize = 8;
- loff_t in_off = 0;
- EXPECT_THAT(splice(in_fd.get(), &in_off, wfd.get(), nullptr, kEventFDSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-// Event FDs may not be used with splice with an offset.
-TEST(SpliceTest, ToEventFDOffset) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill with a value.
- constexpr int kEventFDSize = 8;
- std::vector<char> buf(kEventFDSize);
- buf[0] = 1;
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kEventFDSize));
-
- int efd;
- ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds());
- const FileDescriptor out_fd(efd);
-
- // Attempt to splice 8-byte eventfd value to pipe with offset.
- //
- // This is not allowed because eventfd doesn't support pwrite.
- loff_t out_off = 0;
- EXPECT_THAT(
- splice(rfd.get(), nullptr, out_fd.get(), &out_off, kEventFDSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
TEST(SpliceTest, ToPipe) {
// Open the input file.
const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
@@ -852,34 +777,6 @@ TEST(SpliceTest, FromPipeMaxFileSize) {
EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0);
}
-TEST(SpliceTest, FromPipeToDevZero) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- FileDescriptor wfd(fds[1]);
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- const FileDescriptor zero =
- ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY));
-
- // Close the write end to prevent blocking below.
- wfd.reset();
-
- // Splice to /dev/zero. The first call should empty the pipe, and the return
- // value should not exceed the number of bytes available for reading.
- EXPECT_THAT(
- splice(rfd.get(), nullptr, zero.get(), nullptr, kPageSize + 123, 0),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_THAT(splice(rfd.get(), nullptr, zero.get(), nullptr, 1, 0),
- SyscallSucceedsWithValue(0));
-}
-
static volatile int signaled = 0;
void SigUsr1Handler(int sig, siginfo_t* info, void* context) { signaled = 1; }
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index cb77986c2..bbcb7e4fd 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -2067,6 +2067,69 @@ TEST_P(SimpleTcpSocketTest, GetSocketAcceptConnWithShutdown) {
EXPECT_EQ(got, 0);
}
+void ShutdownConnectingSocket(int domain, int shutdown_mode) {
+ FileDescriptor bound_s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(domain, SOCK_STREAM, IPPROTO_TCP));
+
+ sockaddr_storage bound_addr =
+ ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(domain));
+ socklen_t bound_addrlen = sizeof(bound_addr);
+
+ ASSERT_THAT(bind(bound_s.get(), AsSockAddr(&bound_addr), bound_addrlen),
+ SyscallSucceeds());
+
+ // Start listening. Use a zero backlog to only allow one connection in the
+ // accept queue.
+ ASSERT_THAT(listen(bound_s.get(), 0), SyscallSucceeds());
+
+ // Get the addresses the socket is bound to because the port is chosen by the
+ // stack.
+ ASSERT_THAT(
+ getsockname(bound_s.get(), AsSockAddr(&bound_addr), &bound_addrlen),
+ SyscallSucceeds());
+
+ // Establish a connection. But do not accept it. That way, subsequent
+ // connections will not get a SYN-ACK because the queue is full.
+ FileDescriptor connected_s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(domain, SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(connect(connected_s.get(),
+ reinterpret_cast<const struct sockaddr*>(&bound_addr),
+ bound_addrlen),
+ SyscallSucceeds());
+
+ FileDescriptor connecting_s = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(domain, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
+ ASSERT_THAT(connect(connecting_s.get(),
+ reinterpret_cast<const struct sockaddr*>(&bound_addr),
+ bound_addrlen),
+ SyscallFailsWithErrno(EINPROGRESS));
+
+ // Now the test: when a connecting socket is shutdown, the socket should enter
+ // an error state.
+ EXPECT_THAT(shutdown(connecting_s.get(), shutdown_mode), SyscallSucceeds());
+
+ // We don't need to specify any events to get POLLHUP or POLLERR because these
+ // are always tracked.
+ struct pollfd poll_fd = {
+ .fd = connecting_s.get(),
+ };
+
+ EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), SyscallSucceedsWithValue(1));
+ EXPECT_EQ(poll_fd.revents, POLLHUP | POLLERR);
+}
+
+TEST_P(SimpleTcpSocketTest, ShutdownReadConnectingSocket) {
+ ShutdownConnectingSocket(GetParam(), SHUT_RD);
+}
+
+TEST_P(SimpleTcpSocketTest, ShutdownWriteConnectingSocket) {
+ ShutdownConnectingSocket(GetParam(), SHUT_WR);
+}
+
+TEST_P(SimpleTcpSocketTest, ShutdownReadWriteConnectingSocket) {
+ ShutdownConnectingSocket(GetParam(), SHUT_RDWR);
+}
+
// Tests that connecting to an unspecified address results in ECONNREFUSED.
TEST_P(SimpleTcpSocketTest, ConnectUnspecifiedAddress) {
sockaddr_storage addr;
@@ -2088,6 +2151,126 @@ TEST_P(SimpleTcpSocketTest, ConnectUnspecifiedAddress) {
}
}
+TEST_P(SimpleTcpSocketTest, OnlyAcknowledgeBacklogConnections) {
+ // At some point, there was a bug in gVisor where a connection could be
+ // SYN-ACK'd by the server even if the accept queue was already full. This was
+ // possible because once the listener would process an ACK, it would move the
+ // new connection in the accept queue asynchronously. It created an
+ // opportunity where the listener could process another SYN before completing
+ // the delivery that would have filled the accept queue.
+ //
+ // This test checks that there is no such race.
+
+ std::array<std::optional<ScopedThread>, 100> threads;
+ for (auto& thread : threads) {
+ thread.emplace([]() {
+ FileDescriptor bound_s = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ sockaddr_storage bound_addr =
+ ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
+ socklen_t bound_addrlen = sizeof(bound_addr);
+
+ ASSERT_THAT(bind(bound_s.get(), AsSockAddr(&bound_addr), bound_addrlen),
+ SyscallSucceeds());
+
+ // Start listening. Use a zero backlog to only allow one connection in the
+ // accept queue.
+ ASSERT_THAT(listen(bound_s.get(), 0), SyscallSucceeds());
+
+ // Get the addresses the socket is bound to because the port is chosen by
+ // the stack.
+ ASSERT_THAT(
+ getsockname(bound_s.get(), AsSockAddr(&bound_addr), &bound_addrlen),
+ SyscallSucceeds());
+
+ // Establish a connection, but do not accept it.
+ FileDescriptor connected_s = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(connect(connected_s.get(),
+ reinterpret_cast<const struct sockaddr*>(&bound_addr),
+ bound_addrlen),
+ SyscallSucceeds());
+
+ // Immediately attempt to establish another connection. Use non blocking
+ // socket because this is expected to timeout.
+ FileDescriptor connecting_s = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(GetParam(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
+ ASSERT_THAT(connect(connecting_s.get(),
+ reinterpret_cast<const struct sockaddr*>(&bound_addr),
+ bound_addrlen),
+ SyscallFailsWithErrno(EINPROGRESS));
+
+ struct pollfd poll_fd = {
+ .fd = connecting_s.get(),
+ .events = POLLOUT,
+ };
+ EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10),
+ SyscallSucceedsWithValue(0));
+ });
+ }
+}
+
+// Tests that send will return EWOULDBLOCK initially with large buffer and will
+// succeed after the send buffer size is increased.
+TEST_P(TcpSocketTest, SendUnblocksOnSendBufferIncrease) {
+ // Set the FD to O_NONBLOCK.
+ int opts;
+ ASSERT_THAT(opts = fcntl(first_fd, F_GETFL), SyscallSucceeds());
+ opts |= O_NONBLOCK;
+ ASSERT_THAT(fcntl(first_fd, F_SETFL, opts), SyscallSucceeds());
+
+ // Get maximum buffer size by trying to set it to a large value.
+ constexpr int kSndBufSz = 0xffffffff;
+ ASSERT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &kSndBufSz,
+ sizeof(kSndBufSz)),
+ SyscallSucceeds());
+
+ int max_buffer_sz = 0;
+ socklen_t max_len = sizeof(max_buffer_sz);
+ ASSERT_THAT(
+ getsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &max_buffer_sz, &max_len),
+ SyscallSucceeds());
+
+ int buffer_sz = max_buffer_sz >> 2;
+ EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &buffer_sz,
+ sizeof(buffer_sz)),
+ SyscallSucceedsWithValue(0));
+
+ // Create a large buffer that will be used for sending.
+ std::vector<char> buffer(max_buffer_sz);
+
+ // Write until we receive an error.
+ while (RetryEINTR(send)(first_fd, buffer.data(), buffer.size(), 0) != -1) {
+ // Sleep to give linux a chance to move data from the send buffer to the
+ // receive buffer.
+ usleep(10000); // 10ms.
+ }
+
+ // The last error should have been EWOULDBLOCK.
+ ASSERT_EQ(errno, EWOULDBLOCK);
+
+ ScopedThread send_thread([this]() {
+ int flags = 0;
+ ASSERT_THAT(flags = fcntl(first_fd, F_GETFL), SyscallSucceeds());
+ EXPECT_THAT(fcntl(first_fd, F_SETFL, flags & ~O_NONBLOCK),
+ SyscallSucceeds());
+
+ // Expect the send() to succeed.
+ char buffer;
+ ASSERT_THAT(RetryEINTR(send)(first_fd, &buffer, sizeof(buffer), 0),
+ SyscallSucceeds());
+ });
+
+ // Set SO_SNDBUF to maximum buffer size allowed.
+ buffer_sz = max_buffer_sz >> 1;
+ EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &buffer_sz,
+ sizeof(buffer_sz)),
+ SyscallSucceedsWithValue(0));
+
+ send_thread.Join();
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest,
::testing::Values(AF_INET, AF_INET6));
diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc
index 7c9c5c870..956208bee 100644
--- a/test/syscalls/linux/tuntap.cc
+++ b/test/syscalls/linux/tuntap.cc
@@ -73,29 +73,14 @@ PosixErrorOr<Link> GetLinkByName(const std::string& name) {
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;
+struct ping_ip_pkt {
+ iphdr ip;
+ 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);
+ping_ip_pkt CreatePingIPPacket(const in_addr_t srcip, const in_addr_t dstip) {
+ ping_ip_pkt pkt = {};
pkt.ip.ihl = 5;
pkt.ip.version = 4;
@@ -122,6 +107,33 @@ ping_pkt CreatePingPacket(const uint8_t srcmac[ETH_ALEN], const in_addr_t srcip,
return pkt;
}
+struct pihdr {
+ uint16_t pi_flags;
+ uint16_t pi_protocol;
+} __attribute__((packed));
+
+struct ping_pkt {
+ pihdr pi;
+ ethhdr eth;
+ ping_ip_pkt ip_pkt;
+} __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_pkt = CreatePingIPPacket(srcip, dstip);
+
+ return pkt;
+}
+
struct arp_pkt {
pihdr pi;
struct ethhdr eth;
@@ -274,13 +286,26 @@ TEST_F(TuntapTest, WriteToDownDevice) {
EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EIO));
}
-PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name,
- const in_addr_t dev_addr) {
+struct TunTapInterface {
+ FileDescriptor fd;
+ Link link;
+};
+
+PosixErrorOr<TunTapInterface> OpenAndAttachTunTap(const std::string& dev_name,
+ const in_addr_t dev_addr,
+ bool tap, bool no_pi) {
// Interface creation.
ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, Open(kDevNetTun, O_RDWR));
struct ifreq ifr_set = {};
- ifr_set.ifr_flags = IFF_TAP;
+ if (tap) {
+ ifr_set.ifr_flags |= IFF_TAP;
+ } else {
+ ifr_set.ifr_flags |= IFF_TUN;
+ }
+ if (no_pi) {
+ ifr_set.ifr_flags |= IFF_NO_PI;
+ }
strncpy(ifr_set.ifr_name, dev_name.c_str(), IFNAMSIZ);
if (ioctl(fd.get(), TUNSETIFF, &ifr_set) < 0) {
return PosixError(errno);
@@ -296,13 +321,15 @@ PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name,
if (!IsRunningOnGvisor()) {
// FIXME(b/110961832): gVisor doesn't support setting MAC address on
// interfaces yet.
- RETURN_IF_ERRNO(LinkSetMacAddr(link.index, kMacA, sizeof(kMacA)));
+ if (tap) {
+ 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;
+ return TunTapInterface{.fd = std::move(fd), .link = std::move(link)};
}
// This test sets up a TAP device and pings kernel by sending ICMP echo request.
@@ -322,8 +349,9 @@ PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name,
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));
+ const auto& [fd, link] = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
+
ping_pkt ping_req =
CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr);
std::string arp_rep =
@@ -365,10 +393,10 @@ TEST_F(TuntapTest, PingKernel) {
// 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) {
+ r.ping.ip_pkt.ip.protocol == ping_req.ip_pkt.ip.protocol &&
+ !memcmp(&r.ping.ip_pkt.ip.saddr, &ping_req.ip_pkt.ip.daddr, kIPLen) &&
+ !memcmp(&r.ping.ip_pkt.ip.daddr, &ping_req.ip_pkt.ip.saddr, kIPLen) &&
+ r.ping.ip_pkt.icmp.type == 0 && r.ping.ip_pkt.icmp.code == 0) {
// Ends and passes the test.
break;
}
@@ -378,8 +406,8 @@ TEST_F(TuntapTest, PingKernel) {
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));
+ const auto& [fd, link] = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
// Send a UDP packet to remote.
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
@@ -433,19 +461,17 @@ TEST_F(TuntapTest, TUNNoPacketInfo) {
EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, 24, &dev_ipv4_addr,
sizeof(dev_ipv4_addr)));
- ping_pkt ping_req =
- CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr);
- size_t packet_size = sizeof(ping_req) - offsetof(ping_pkt, ip);
+ ping_ip_pkt ping_req = CreatePingIPPacket(kTapPeerIPAddr, kTapIPAddr);
// Send ICMP query
- EXPECT_THAT(write(fd.get(), &ping_req.ip, packet_size),
- SyscallSucceedsWithValue(packet_size));
+ EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
// Receive loop to process inbound packets.
while (1) {
- ping_pkt ping_resp = {};
- EXPECT_THAT(read(fd.get(), &ping_resp.ip, packet_size),
- SyscallSucceedsWithValue(packet_size));
+ ping_ip_pkt ping_resp = {};
+ EXPECT_THAT(read(fd.get(), &ping_resp, sizeof(ping_req)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
// Process ping response packet.
if (!memcmp(&ping_resp.ip.saddr, &ping_req.ip.daddr, kIPLen) &&
@@ -465,8 +491,8 @@ TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) {
FileDescriptor sender =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
+ const auto tuntap = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
sockaddr_in connect_addr = {
.sin_family = AF_INET,
@@ -487,8 +513,8 @@ TEST_F(TuntapTest, TCPNonBlockingConnectFailsArpResolution) {
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));
+ const auto tuntap = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
sockaddr_in connect_addr = {
.sin_family = AF_INET,
@@ -518,8 +544,8 @@ TEST_F(TuntapTest, TCPNonBlockingConnectFailsArpResolution) {
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));
+ const auto tuntap = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
int sock = socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_THAT(sock, SyscallSucceeds());
@@ -534,5 +560,95 @@ TEST_F(TuntapTest, WriteHangBug155928773) {
write(sock, "hello", 5);
}
+// Test that raw packet sockets do not need/include link headers when
+// sending/receiving packets to/from pure L3 (e.g. TUN) interfaces.
+TEST_F(TuntapTest, RawPacketSocket) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ auto [tun, link] = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTunName, kTapIPAddr, false /* tap */, true /* no_pi */));
+ FileDescriptor packet_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)));
+
+ constexpr int kInfiniteTimeout = -1;
+
+ uint8_t hardware_address_length = 0;
+ if (IsRunningOnGvisor()) {
+ // TODO(https://gvisor.dev/issue/6530): Do not assume all interfaces have
+ // an ethernet address.
+ hardware_address_length = ETH_ALEN;
+ }
+
+ {
+ const ping_ip_pkt ping_req = CreatePingIPPacket(kTapPeerIPAddr, kTapIPAddr);
+ ASSERT_THAT(write(tun.get(), &ping_req, sizeof(ping_req)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+ // Wait for the packet socket to become readable.
+ pollfd pfd = {
+ .fd = packet_sock.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfiniteTimeout),
+ SyscallSucceedsWithValue(1));
+
+ char read_buf[sizeof(ping_req) + 1];
+ struct sockaddr_ll src;
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(packet_sock.get(), read_buf, sizeof(read_buf), 0,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+ EXPECT_EQ(memcmp(read_buf, &ping_req, sizeof(ping_req)), 0);
+ ASSERT_EQ(src_len, sizeof(src));
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ EXPECT_EQ(src.sll_ifindex, link.index);
+ EXPECT_EQ(src.sll_pkttype, PACKET_HOST);
+ EXPECT_EQ(src.sll_halen, hardware_address_length);
+ if (IsRunningOnGvisor()) {
+ // TODO(https://gvisor.dev/issue/6531): Check this field for the right
+ // hardware type.
+ EXPECT_EQ(src.sll_hatype, 0);
+ } else {
+ EXPECT_EQ(src.sll_hatype, ARPHRD_NONE);
+ }
+ }
+
+ {
+ const struct sockaddr_ll dest = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_IP),
+ .sll_ifindex = link.index,
+ .sll_halen = hardware_address_length,
+ };
+
+ const ping_ip_pkt ping_req = CreatePingIPPacket(kTapIPAddr, kTapPeerIPAddr);
+ ASSERT_THAT(
+ sendto(packet_sock.get(), &ping_req, sizeof(ping_req), 0,
+ reinterpret_cast<const struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+
+ // Loop until we receive the packet we expect - the kernel may send packets
+ // we do not care about.
+ while (true) {
+ // Wait for the TUN interface to become readable.
+ pollfd pfd = {
+ .fd = tun.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfiniteTimeout),
+ SyscallSucceedsWithValue(1));
+
+ char read_buf[sizeof(ping_req) + 1];
+ int n = read(tun.get(), &read_buf, sizeof(read_buf));
+ ASSERT_THAT(n, SyscallSucceeds());
+ if (n == sizeof(ping_req) &&
+ memcmp(read_buf, &ping_req, sizeof(ping_req)) == 0) {
+ break;
+ }
+ }
+ }
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc
index 3353e58cb..0c0a16074 100644
--- a/test/syscalls/linux/udp_socket.cc
+++ b/test/syscalls/linux/udp_socket.cc
@@ -18,6 +18,8 @@
#include <netinet/ip_icmp.h>
#include <ctime>
+#include <utility>
+#include <vector>
#ifdef __linux__
#include <linux/errqueue.h>
@@ -600,6 +602,67 @@ TEST_P(UdpSocketTest, DisconnectAfterBind) {
SyscallFailsWithErrno(ENOTCONN));
}
+void ConnectThenDisconnect(const FileDescriptor& sock,
+ const sockaddr* bind_addr,
+ const socklen_t expected_addrlen) {
+ // Connect the bound socket.
+ ASSERT_THAT(connect(sock.get(), bind_addr, expected_addrlen),
+ SyscallSucceeds());
+
+ // Disconnect.
+ {
+ sockaddr_storage unspec = {.ss_family = AF_UNSPEC};
+ ASSERT_THAT(connect(sock.get(), AsSockAddr(&unspec), sizeof(unspec)),
+ SyscallSucceeds());
+ }
+ {
+ // Check that we're not in a bound state.
+ sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ ASSERT_THAT(getsockname(sock.get(), AsSockAddr(&addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_EQ(addrlen, expected_addrlen);
+ // Everything should be the zero value except the address family.
+ sockaddr_storage expected = {
+ .ss_family = bind_addr->sa_family,
+ };
+ EXPECT_EQ(memcmp(&expected, &addr, expected_addrlen), 0);
+ }
+
+ {
+ // We are not connected so we have no peer.
+ sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ EXPECT_THAT(getpeername(sock.get(), AsSockAddr(&addr), &addrlen),
+ SyscallFailsWithErrno(ENOTCONN));
+ }
+}
+
+TEST_P(UdpSocketTest, DisconnectAfterBindToUnspecAndConnect) {
+ ASSERT_NO_ERRNO(BindLoopback());
+
+ sockaddr_storage unspec = {.ss_family = AF_UNSPEC};
+ int bind_res = bind(sock_.get(), AsSockAddr(&unspec), sizeof(unspec));
+ if ((!IsRunningOnGvisor() || IsRunningWithHostinet()) &&
+ GetFamily() == AF_INET) {
+ // Linux allows this for undocumented compatibility reasons:
+ // https://github.com/torvalds/linux/commit/29c486df6a208432b370bd4be99ae1369ede28d8.
+ //
+ // TODO(https://gvisor.dev/issue/6575): Match Linux's behaviour.
+ ASSERT_THAT(bind_res, SyscallSucceeds());
+ } else {
+ ASSERT_THAT(bind_res, SyscallFailsWithErrno(EAFNOSUPPORT));
+ }
+
+ ASSERT_NO_FATAL_FAILURE(ConnectThenDisconnect(sock_, bind_addr_, addrlen_));
+}
+
+TEST_P(UdpSocketTest, DisconnectAfterConnectWithoutBind) {
+ ASSERT_NO_ERRNO(BindLoopback());
+
+ ASSERT_NO_FATAL_FAILURE(ConnectThenDisconnect(sock_, bind_addr_, addrlen_));
+}
+
TEST_P(UdpSocketTest, BindToAnyConnnectToLocalhost) {
ASSERT_NO_ERRNO(BindAny());
@@ -1685,6 +1748,148 @@ TEST_P(UdpSocketTest, TimestampIoctlPersistence) {
ASSERT_EQ(tv.tv_usec, tv2.tv_usec);
}
+// TOS and TCLASS values may be different but IPv6 sockets with IPv4-mapped-IPv6
+// addresses use TOS (IPv4), not TCLASS (IPv6).
+TEST_P(UdpSocketTest, DifferentTOSAndTClass) {
+ const int kFamily = GetFamily();
+ constexpr int kToS = IPTOS_LOWDELAY;
+ constexpr int kTClass = IPTOS_THROUGHPUT;
+ ASSERT_NE(kToS, kTClass);
+
+ if (kFamily == AF_INET6) {
+ ASSERT_THAT(setsockopt(sock_.get(), SOL_IPV6, IPV6_TCLASS, &kTClass,
+ sizeof(kTClass)),
+ SyscallSucceeds());
+
+ // Marking an IPv6 socket as IPv6 only should not affect the ability to
+ // configure IPv4 socket options as the V6ONLY flag may later be disabled so
+ // that applications may use the socket to send/receive IPv4 packets.
+ constexpr int on = 1;
+ ASSERT_THAT(setsockopt(sock_.get(), SOL_IPV6, IPV6_V6ONLY, &on, sizeof(on)),
+ SyscallSucceeds());
+ }
+
+ ASSERT_THAT(setsockopt(sock_.get(), SOL_IP, IP_TOS, &kToS, sizeof(kToS)),
+ SyscallSucceeds());
+
+ if (kFamily == AF_INET6) {
+ int got_tclass;
+ socklen_t got_tclass_len = sizeof(got_tclass);
+ ASSERT_THAT(getsockopt(sock_.get(), SOL_IPV6, IPV6_TCLASS, &got_tclass,
+ &got_tclass_len),
+ SyscallSucceeds());
+ ASSERT_EQ(got_tclass_len, sizeof(got_tclass));
+ EXPECT_EQ(got_tclass, kTClass);
+ }
+
+ {
+ int got_tos;
+ socklen_t got_tos_len = sizeof(got_tos);
+ ASSERT_THAT(getsockopt(sock_.get(), SOL_IP, IP_TOS, &got_tos, &got_tos_len),
+ SyscallSucceeds());
+ ASSERT_EQ(got_tos_len, sizeof(got_tos));
+ EXPECT_EQ(got_tos, kToS);
+ }
+
+ auto test_send = [this](sockaddr_storage addr,
+ std::function<void(const cmsghdr*)> cb) {
+ FileDescriptor bind = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(addr.ss_family, SOCK_DGRAM, IPPROTO_UDP));
+ ASSERT_NO_ERRNO(BindSocket(bind.get(), reinterpret_cast<sockaddr*>(&addr)));
+ ASSERT_THAT(setsockopt(bind.get(), SOL_IP, IP_RECVTOS, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ if (addr.ss_family == AF_INET6) {
+ ASSERT_THAT(setsockopt(bind.get(), SOL_IPV6, IPV6_RECVTCLASS, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ }
+
+ char sent_data[1024];
+ iovec sent_iov = {
+ .iov_base = sent_data,
+ .iov_len = sizeof(sent_data),
+ };
+ msghdr sent_msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(addr),
+ .msg_iov = &sent_iov,
+ .msg_iovlen = 1,
+ };
+ ASSERT_THAT(RetryEINTR(sendmsg)(sock_.get(), &sent_msg, 0),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ char received_data[sizeof(sent_data) + 1];
+ iovec received_iov = {
+ .iov_base = received_data,
+ .iov_len = sizeof(received_data),
+ };
+ std::vector<char> received_cmsgbuf(CMSG_SPACE(sizeof(int8_t)));
+ msghdr received_msg = {
+ .msg_iov = &received_iov,
+ .msg_iovlen = 1,
+ .msg_control = received_cmsgbuf.data(),
+ .msg_controllen = static_cast<socklen_t>(received_cmsgbuf.size()),
+ };
+ ASSERT_THAT(RetryEINTR(recvmsg)(bind.get(), &received_msg, 0),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg);
+ ASSERT_NE(cmsg, nullptr);
+ ASSERT_NO_FATAL_FAILURE(cb(cmsg));
+ EXPECT_EQ(CMSG_NXTHDR(&received_msg, cmsg), nullptr);
+ };
+
+ if (kFamily == AF_INET6) {
+ SCOPED_TRACE(
+ "Send IPv4 loopback packet using IPv6 socket via IPv4-mapped-IPv6");
+
+ constexpr int off = 0;
+ ASSERT_THAT(
+ setsockopt(sock_.get(), SOL_IPV6, IPV6_V6ONLY, &off, sizeof(off)),
+ SyscallSucceeds());
+
+ // Send a packet and make sure that the ToS value in the IPv4 header is
+ // the configured IPv4 ToS Value and not the IPv6 Traffic Class value even
+ // though we use an IPv6 socket to send an IPv4 packet.
+ ASSERT_NO_FATAL_FAILURE(
+ test_send(V4MappedLoopback().addr, [kToS](const cmsghdr* cmsg) {
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int8_t)));
+ EXPECT_EQ(cmsg->cmsg_level, SOL_IP);
+ EXPECT_EQ(cmsg->cmsg_type, IP_TOS);
+ int8_t received;
+ memcpy(&received, CMSG_DATA(cmsg), sizeof(received));
+ EXPECT_EQ(received, kToS);
+ }));
+ }
+
+ {
+ SCOPED_TRACE("Send loopback packet");
+
+ ASSERT_NO_FATAL_FAILURE(test_send(
+ InetLoopbackAddr(), [kFamily, kTClass, kToS](const cmsghdr* cmsg) {
+ switch (kFamily) {
+ case AF_INET: {
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int8_t)));
+ EXPECT_EQ(cmsg->cmsg_level, SOL_IP);
+ EXPECT_EQ(cmsg->cmsg_type, IP_TOS);
+ int8_t received;
+ memcpy(&received, CMSG_DATA(cmsg), sizeof(received));
+ EXPECT_EQ(received, kToS);
+ } break;
+ case AF_INET6: {
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int32_t)));
+ EXPECT_EQ(cmsg->cmsg_level, SOL_IPV6);
+ EXPECT_EQ(cmsg->cmsg_type, IPV6_TCLASS);
+ int32_t received;
+ memcpy(&received, CMSG_DATA(cmsg), sizeof(received));
+ EXPECT_EQ(received, kTClass);
+ } break;
+ }
+ }));
+ }
+}
+
// Test that a socket with IP_TOS or IPV6_TCLASS set will set the TOS byte on
// outgoing packets, and that a receiving socket with IP_RECVTOS or
// IPV6_RECVTCLASS will create the corresponding control message.
diff --git a/test/util/BUILD b/test/util/BUILD
index 4a4401ba8..5b6bcb32c 100644
--- a/test/util/BUILD
+++ b/test/util/BUILD
@@ -1,4 +1,4 @@
-load("//tools:defs.bzl", "cc_library", "cc_test", "coreutil", "default_net_util", "gbenchmark", "gtest", "select_system")
+load("//tools:defs.bzl", "cc_library", "cc_test", "coreutil", "default_net_util", "gbenchmark_internal", "gtest", "select_system")
package(
default_visibility = ["//:sandbox"],
@@ -8,13 +8,20 @@ package(
cc_library(
name = "capability_util",
testonly = 1,
- srcs = ["capability_util.cc"],
- hdrs = ["capability_util.h"],
+ srcs = [
+ "fuchsia_capability_util.cc",
+ "linux_capability_util.cc",
+ ],
+ hdrs = [
+ "capability_util.h",
+ "linux_capability_util.h",
+ ],
deps = [
":cleanup",
":memory_util",
":posix_error",
":save_util",
+ ":socket_util",
":test_util",
"@com_google_absl//absl/strings",
],
@@ -288,7 +295,7 @@ cc_library(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
gtest,
- gbenchmark,
+ gbenchmark_internal,
],
)
@@ -343,6 +350,18 @@ cc_library(
)
cc_library(
+ name = "benchmark_main",
+ testonly = 1,
+ linkstatic = 1,
+ deps = [
+ ":test_util",
+ "@com_google_absl//absl/flags:flag",
+ gtest,
+ gbenchmark_internal,
+ ],
+)
+
+cc_library(
name = "epoll_util",
testonly = 1,
srcs = ["epoll_util.cc"],
diff --git a/test/util/capability_util.h b/test/util/capability_util.h
index c4b0feade..ac1a1b32b 100644
--- a/test/util/capability_util.h
+++ b/test/util/capability_util.h
@@ -17,112 +17,32 @@
#ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
#define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
-#ifdef __linux__
-
-#include <errno.h>
-#include <linux/capability.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include "test/util/cleanup.h"
#include "test/util/posix_error.h"
-#include "test/util/save_util.h"
-#include "test/util/test_util.h"
-#ifndef _LINUX_CAPABILITY_VERSION_3
-#error Expecting _LINUX_CAPABILITY_VERSION_3 support
+#if defined(__Fuchsia__)
+// Nothing to include.
+#elif defined(__linux__)
+#include "test/util/linux_capability_util.h"
+#else
+#error "Unhandled platform"
#endif
namespace gvisor {
namespace testing {
-// HaveCapability returns true if the process has the specified EFFECTIVE
-// capability.
-inline PosixErrorOr<bool> HaveCapability(int cap) {
- if (!cap_valid(cap)) {
- return PosixError(EINVAL, "Invalid capability");
- }
-
- struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
- struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
- MaybeSave();
-
- return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0;
-}
-
-// SetCapability sets the specified EFFECTIVE capability.
-inline PosixError SetCapability(int cap, bool set) {
- if (!cap_valid(cap)) {
- return PosixError(EINVAL, "Invalid capability");
- }
-
- struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
- struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
- MaybeSave();
-
- if (set) {
- caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap);
- } else {
- caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
- }
- header = {_LINUX_CAPABILITY_VERSION_3, 0};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
- MaybeSave();
-
- return NoError();
-}
-
-// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE
-// capabilities must be a subset of PERMITTED, so those are dropped as well.
-inline PosixError DropPermittedCapability(int cap) {
- if (!cap_valid(cap)) {
- return PosixError(EINVAL, "Invalid capability");
- }
-
- struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
- struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
- MaybeSave();
-
- caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
- caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap);
-
- header = {_LINUX_CAPABILITY_VERSION_3, 0};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
- MaybeSave();
-
- return NoError();
-}
-
-PosixErrorOr<bool> CanCreateUserNamespace();
-
-class AutoCapability {
- public:
- AutoCapability(int cap, bool set) : cap_(cap), set_(set) {
- const bool has = EXPECT_NO_ERRNO_AND_VALUE(HaveCapability(cap));
- if (set != has) {
- EXPECT_NO_ERRNO(SetCapability(cap_, set_));
- applied_ = true;
- }
- }
-
- ~AutoCapability() {
- if (applied_) {
- EXPECT_NO_ERRNO(SetCapability(cap_, !set_));
- }
- }
+// HaveRawIPSocketCapability returns whether or not the process has access to
+// raw IP sockets.
+//
+// Returns an error when raw IP socket access cannot be determined.
+PosixErrorOr<bool> HaveRawIPSocketCapability();
- private:
- int cap_;
- bool set_;
- bool applied_ = false;
-};
+// HavePacketSocketCapability returns whether or not the process has access to
+// packet sockets.
+//
+// Returns an error when packet socket access cannot be determined.
+PosixErrorOr<bool> HavePacketSocketCapability();
} // namespace testing
} // namespace gvisor
-#endif // __linux__
-
#endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
diff --git a/test/util/cgroup_util.cc b/test/util/cgroup_util.cc
index 977993f41..df3c57b87 100644
--- a/test/util/cgroup_util.cc
+++ b/test/util/cgroup_util.cc
@@ -25,12 +25,26 @@
namespace gvisor {
namespace testing {
-Cgroup::Cgroup(std::string_view path) : cgroup_path_(path) {
+Cgroup::Cgroup(absl::string_view path) : cgroup_path_(path) {
id_ = ++Cgroup::next_id_;
std::cerr << absl::StreamFormat("[cg#%d] <= %s", id_, cgroup_path_)
<< std::endl;
}
+PosixErrorOr<Cgroup> Cgroup::RecursivelyCreate(absl::string_view path) {
+ RETURN_IF_ERRNO(RecursivelyCreateDir(path));
+ return Cgroup(path);
+}
+
+PosixErrorOr<Cgroup> Cgroup::Create(absl::string_view path) {
+ RETURN_IF_ERRNO(Mkdir(path));
+ return Cgroup(path);
+}
+
+PosixErrorOr<Cgroup> Cgroup::CreateChild(absl::string_view name) const {
+ return Cgroup::Create(JoinPath(Path(), name));
+}
+
PosixErrorOr<std::string> Cgroup::ReadControlFile(
absl::string_view name) const {
std::string buf;
@@ -93,7 +107,7 @@ PosixErrorOr<absl::flat_hash_set<pid_t>> Cgroup::ParsePIDList(
absl::string_view data) const {
absl::flat_hash_set<pid_t> res;
std::vector<absl::string_view> lines = absl::StrSplit(data, '\n');
- for (const std::string_view& line : lines) {
+ for (const absl::string_view& line : lines) {
if (line.empty()) {
continue;
}
diff --git a/test/util/cgroup_util.h b/test/util/cgroup_util.h
index e3f696a89..ccc7219e3 100644
--- a/test/util/cgroup_util.h
+++ b/test/util/cgroup_util.h
@@ -34,8 +34,20 @@ class Cgroup {
uint64_t id() const { return id_; }
+ // RecursivelyCreate creates cgroup specified by path, including all
+ // components leading up to path. Path should end inside a cgroupfs mount. If
+ // path already exists, RecursivelyCreate does nothing and silently succeeds.
+ static PosixErrorOr<Cgroup> RecursivelyCreate(std::string_view path);
+
+ // Creates a new cgroup at path. The parent directory must exist and be a
+ // cgroupfs directory.
+ static PosixErrorOr<Cgroup> Create(std::string_view path);
+
const std::string& Path() const { return cgroup_path_; }
+ // Creates a child cgroup under this cgroup with the given name.
+ PosixErrorOr<Cgroup> CreateChild(std::string_view name) const;
+
std::string Relpath(absl::string_view leaf) const {
return JoinPath(cgroup_path_, leaf);
}
diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc
index 483ae848d..253411858 100644
--- a/test/util/fs_util.cc
+++ b/test/util/fs_util.cc
@@ -201,7 +201,8 @@ PosixError UnlinkAt(const FileDescriptor& dfd, absl::string_view path,
PosixError Mkdir(absl::string_view path, int mode) {
int res = mkdir(std::string(path).c_str(), mode);
if (res < 0) {
- return PosixError(errno, absl::StrCat("mkdir ", path, " mode ", mode));
+ return PosixError(errno,
+ absl::StrFormat("mkdir \"%s\" mode %#o", path, mode));
}
return NoError();
diff --git a/test/util/fuchsia_capability_util.cc b/test/util/fuchsia_capability_util.cc
new file mode 100644
index 000000000..bbe8643e7
--- /dev/null
+++ b/test/util/fuchsia_capability_util.cc
@@ -0,0 +1,73 @@
+// Copyright 2021 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.
+
+#ifdef __Fuchsia__
+
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "test/util/socket_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// On Linux, access to raw IP and packet socket is controlled by a single
+// capability (CAP_NET_RAW). However on Fuchsia, access to raw IP and packet
+// sockets are controlled by separate capabilities/protocols.
+
+namespace {
+
+PosixErrorOr<bool> HaveSocketCapability(int domain, int type, int protocol) {
+ // Fuchsia does not have a platform supported way to check the protocols made
+ // available to a sandbox. As a workaround, simply try to create the specified
+ // socket and assume no access if we get a no permissions error.
+ auto s = Socket(domain, type, protocol);
+ if (s.ok()) {
+ return true;
+ }
+ if (s.error().errno_value() == EPERM) {
+ return false;
+ }
+ return s.error();
+}
+
+} // namespace
+
+PosixErrorOr<bool> HaveRawIPSocketCapability() {
+ static PosixErrorOr<bool> result(false);
+ static std::once_flag once;
+
+ std::call_once(once, [&]() {
+ result = HaveSocketCapability(AF_INET, SOCK_RAW, IPPROTO_UDP);
+ });
+
+ return result;
+}
+
+PosixErrorOr<bool> HavePacketSocketCapability() {
+ static PosixErrorOr<bool> result(false);
+ static std::once_flag once;
+
+ std::call_once(once, [&]() {
+ result = HaveSocketCapability(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ });
+
+ return result;
+}
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // __Fuchsia__
diff --git a/test/util/capability_util.cc b/test/util/linux_capability_util.cc
index 3bf218128..7218aa4ac 100644
--- a/test/util/capability_util.cc
+++ b/test/util/linux_capability_util.cc
@@ -14,7 +14,7 @@
#ifdef __linux__
-#include "test/util/capability_util.h"
+#include "test/util/linux_capability_util.h"
#include <linux/capability.h>
#include <sched.h>
@@ -32,6 +32,14 @@
namespace gvisor {
namespace testing {
+PosixErrorOr<bool> HaveRawIPSocketCapability() {
+ return HaveCapability(CAP_NET_RAW);
+}
+
+PosixErrorOr<bool> HavePacketSocketCapability() {
+ return HaveCapability(CAP_NET_RAW);
+}
+
PosixErrorOr<bool> CanCreateUserNamespace() {
// The most reliable way to determine if userns creation is possible is by
// trying to create one; see below.
diff --git a/test/util/linux_capability_util.h b/test/util/linux_capability_util.h
new file mode 100644
index 000000000..be94ebd19
--- /dev/null
+++ b/test/util/linux_capability_util.h
@@ -0,0 +1,128 @@
+// Copyright 2018 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.
+
+// Utilities for testing capabilities on Linux.
+
+#ifndef GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_
+#define GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_
+
+#ifdef __linux__
+
+#include <errno.h>
+#include <linux/capability.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "test/util/cleanup.h"
+#include "test/util/posix_error.h"
+#include "test/util/save_util.h"
+#include "test/util/test_util.h"
+
+#ifndef _LINUX_CAPABILITY_VERSION_3
+#error Expecting _LINUX_CAPABILITY_VERSION_3 support
+#endif
+
+namespace gvisor {
+namespace testing {
+
+// HaveCapability returns true if the process has the specified EFFECTIVE
+// capability.
+inline PosixErrorOr<bool> HaveCapability(int cap) {
+ if (!cap_valid(cap)) {
+ return PosixError(EINVAL, "Invalid capability");
+ }
+
+ struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
+ MaybeSave();
+
+ return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0;
+}
+
+// SetCapability sets the specified EFFECTIVE capability.
+inline PosixError SetCapability(int cap, bool set) {
+ if (!cap_valid(cap)) {
+ return PosixError(EINVAL, "Invalid capability");
+ }
+
+ struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
+ MaybeSave();
+
+ if (set) {
+ caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap);
+ } else {
+ caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
+ }
+ header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
+ MaybeSave();
+
+ return NoError();
+}
+
+// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE
+// capabilities must be a subset of PERMITTED, so those are dropped as well.
+inline PosixError DropPermittedCapability(int cap) {
+ if (!cap_valid(cap)) {
+ return PosixError(EINVAL, "Invalid capability");
+ }
+
+ struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
+ MaybeSave();
+
+ caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
+ caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap);
+
+ header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
+ MaybeSave();
+
+ return NoError();
+}
+
+PosixErrorOr<bool> CanCreateUserNamespace();
+
+class AutoCapability {
+ public:
+ AutoCapability(int cap, bool set) : cap_(cap), set_(set) {
+ const bool has = EXPECT_NO_ERRNO_AND_VALUE(HaveCapability(cap));
+ if (set != has) {
+ EXPECT_NO_ERRNO(SetCapability(cap_, set_));
+ applied_ = true;
+ }
+ }
+
+ ~AutoCapability() {
+ if (applied_) {
+ EXPECT_NO_ERRNO(SetCapability(cap_, !set_));
+ }
+ }
+
+ private:
+ int cap_;
+ bool set_;
+ bool applied_ = false;
+};
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // __linux__
+
+#endif // GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_
diff --git a/test/util/socket_util.cc b/test/util/socket_util.cc
index f2360b732..650b422ae 100644
--- a/test/util/socket_util.cc
+++ b/test/util/socket_util.cc
@@ -57,7 +57,19 @@ Creator<FileDescriptor> SyscallSocketCreator(int domain, int type,
PosixErrorOr<struct sockaddr_un> UniqueUnixAddr(bool abstract, int domain) {
struct sockaddr_un addr = {};
+
+#ifdef ANDROID
+ // Using NewTempAbsPath() can cause the tmp directory path to exceed the max
+ // length (i.e., sizeof(addr.sun_path)).
+ //
+ // However, existing systems that are built with the ANDROID configuration
+ // have their temp directory in a different location, and must respect the
+ // TEST_TMPDIR.
+ std::string path = NewTempAbsPath();
+#else
std::string path = NewTempAbsPathInDir("/tmp");
+#endif // ANDROID
+
if (path.size() >= sizeof(addr.sun_path)) {
return PosixError(EINVAL,
"Unable to generate a temp path of appropriate length");
diff --git a/test/util/socket_util.h b/test/util/socket_util.h
index 0e2be63cc..588f041b7 100644
--- a/test/util/socket_util.h
+++ b/test/util/socket_util.h
@@ -554,15 +554,27 @@ uint16_t ICMPChecksum(struct icmphdr icmphdr, const char* payload,
inline sockaddr* AsSockAddr(sockaddr_storage* s) {
return reinterpret_cast<sockaddr*>(s);
}
+inline const sockaddr* AsSockAddr(const sockaddr_storage* s) {
+ return reinterpret_cast<const sockaddr*>(s);
+}
inline sockaddr* AsSockAddr(sockaddr_in* s) {
return reinterpret_cast<sockaddr*>(s);
}
+inline const sockaddr* AsSockAddr(const sockaddr_in* s) {
+ return reinterpret_cast<const sockaddr*>(s);
+}
inline sockaddr* AsSockAddr(sockaddr_in6* s) {
return reinterpret_cast<sockaddr*>(s);
}
+inline const sockaddr* AsSockAddr(const sockaddr_in6* s) {
+ return reinterpret_cast<const sockaddr*>(s);
+}
inline sockaddr* AsSockAddr(sockaddr_un* s) {
return reinterpret_cast<sockaddr*>(s);
}
+inline const sockaddr* AsSockAddr(const sockaddr_un* s) {
+ return reinterpret_cast<const sockaddr*>(s);
+}
PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr);