From f37b20c01111a4f3c43aed5dfb4e1292aa4c91fa Mon Sep 17 00:00:00 2001 From: Nicolas Lacasse Date: Fri, 3 Mar 2023 13:01:59 -0800 Subject: [PATCH] hostinet: Support ping and raw sockets. PiperOrigin-RevId: 513902015 --- Makefile | 6 ++-- pkg/abi/linux/socket.go | 3 ++ pkg/sentry/socket/hostinet/BUILD | 1 + pkg/sentry/socket/hostinet/socket.go | 35 ++++++++++++++++--- pkg/sentry/socket/hostinet/sockopt.go | 5 +++ pkg/sentry/socket/hostinet/stack.go | 9 ++++- pkg/sentry/strace/socket.go | 1 + pkg/test/testutil/testutil.go | 22 ++++++++---- runsc/boot/filter/extra_filters_hostinet.go | 8 +++-- runsc/boot/filter/filter.go | 23 ++++++++----- runsc/boot/loader.go | 18 +++++----- test/e2e/exec_test.go | 37 ++++++++++++++++----- test/e2e/integration_test.go | 6 ---- test/syscalls/BUILD | 4 +++ test/syscalls/linux/raw_socket.cc | 4 +-- test/syscalls/linux/raw_socket_icmp.cc | 21 ++++++++++-- 16 files changed, 148 insertions(+), 55 deletions(-) diff --git a/Makefile b/Makefile index 8308bc566..360ff91d4 100644 --- a/Makefile +++ b/Makefile @@ -286,8 +286,8 @@ swgso-tests: load-basic $(RUNTIME_BIN) .PHONY: swgso-tests hostnet-tests: load-basic $(RUNTIME_BIN) - @$(call install_runtime,$(RUNTIME),--network=host) - @$(call test_runtime,$(RUNTIME),--test_env=CHECKPOINT=false --test_env=HOSTNET=true $(INTEGRATION_TARGETS)) + @$(call install_runtime,$(RUNTIME),--network=host --net-raw) + @$(call test_runtime,$(RUNTIME),--test_env=TEST_CHECKPOINT=false --test_env=TEST_HOSTNET=true --test_env=TEST_NET_RAW=true $(INTEGRATION_TARGETS)) .PHONY: hostnet-tests kvm-tests: load-basic $(RUNTIME_BIN) @@ -311,7 +311,7 @@ iptables-tests: load-iptables $(RUNTIME_BIN) @# FIXME(b/218923513): Need to fix permissions issues. @#$(call test,--test_env=RUNTIME=runc //test/iptables:iptables_test) @$(call install_runtime,$(RUNTIME),--net-raw) - @$(call test_runtime,$(RUNTIME),//test/iptables:iptables_test) + @$(call test_runtime,$(RUNTIME),--test_env=TEST_NET_RAW=true //test/iptables:iptables_test) .PHONY: iptables-tests packetdrill-tests: load-packetdrill $(RUNTIME_BIN) diff --git a/pkg/abi/linux/socket.go b/pkg/abi/linux/socket.go index 1c74ea2b8..0231793fd 100644 --- a/pkg/abi/linux/socket.go +++ b/pkg/abi/linux/socket.go @@ -603,3 +603,6 @@ const SO_ACCEPTCON = 1 << 16 type ICMP6Filter struct { Filter [8]uint32 } + +// SizeOfICMP6Filter is the size of ICMP6Filter struct. +var SizeOfICMP6Filter = uint32((*ICMP6Filter)(nil).SizeBytes()) diff --git a/pkg/sentry/socket/hostinet/BUILD b/pkg/sentry/socket/hostinet/BUILD index 4671bd94f..e9c96c56d 100644 --- a/pkg/sentry/socket/hostinet/BUILD +++ b/pkg/sentry/socket/hostinet/BUILD @@ -34,6 +34,7 @@ go_library( "//pkg/sentry/hostfd", "//pkg/sentry/inet", "//pkg/sentry/kernel", + "//pkg/sentry/kernel/auth", "//pkg/sentry/kernel/time", "//pkg/sentry/socket", "//pkg/sentry/socket/control", diff --git a/pkg/sentry/socket/hostinet/socket.go b/pkg/sentry/socket/hostinet/socket.go index 1c013c2bd..9b4a64d14 100644 --- a/pkg/sentry/socket/hostinet/socket.go +++ b/pkg/sentry/socket/hostinet/socket.go @@ -29,6 +29,7 @@ import ( "gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs" "gvisor.dev/gvisor/pkg/sentry/hostfd" "gvisor.dev/gvisor/pkg/sentry/kernel" + "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time" "gvisor.dev/gvisor/pkg/sentry/socket" "gvisor.dev/gvisor/pkg/sentry/socket/control" @@ -66,6 +67,21 @@ var AllowedSocketTypes = []AllowedSocketType{ {unix.AF_INET6, unix.SOCK_STREAM, unix.IPPROTO_TCP}, {unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_UDP}, + {unix.AF_INET6, unix.SOCK_DGRAM, unix.IPPROTO_ICMPV6}, +} + +// AllowedRawSocketTypes are the socket types which are supported by hostinet +// with raw sockets enabled. +var AllowedRawSocketTypes = []AllowedSocketType{ + {unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_RAW}, + {unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_TCP}, + {unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_UDP}, + {unix.AF_INET, unix.SOCK_RAW, unix.IPPROTO_ICMP}, + + {unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_RAW}, + {unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_TCP}, + {unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_UDP}, + {unix.AF_INET6, unix.SOCK_RAW, unix.IPPROTO_ICMPV6}, } // Socket implements socket.Socket (and by extension, vfs.FileDescriptionImpl) @@ -185,16 +201,24 @@ type socketProvider struct { // Socket implements socket.Provider.Socket. func (p *socketProvider) Socket(t *kernel.Task, stypeflags linux.SockType, protocol int) (*vfs.FileDescription, *syserr.Error) { // Check that we are using the host network stack. - stack := t.NetworkContext() - if stack == nil { + netCtx := t.NetworkContext() + if netCtx == nil { return nil, nil } - if _, ok := stack.(*Stack); !ok { + stack, ok := netCtx.(*Stack) + if !ok { return nil, nil } stype := stypeflags & linux.SOCK_TYPE_MASK + // Raw sockets require CAP_NET_RAW. + if stype == linux.SOCK_RAW { + if creds := auth.CredentialsFromContext(t); !creds.HasCapability(linux.CAP_NET_RAW) { + return nil, syserr.ErrNotPermitted + } + } + // Convert generic IPPROTO_IP protocol to the actual protocol depending // on family and type. if protocol == linux.IPPROTO_IP && (p.family == linux.AF_INET || p.family == linux.AF_INET6) { @@ -208,7 +232,7 @@ func (p *socketProvider) Socket(t *kernel.Task, stypeflags linux.SockType, proto // Validate the socket based on family, type, and protocol. var supported bool - for _, allowed := range AllowedSocketTypes { + for _, allowed := range stack.allowedSocketTypes { if p.family == allowed.Family && int(stype) == allowed.Type && protocol == allowed.Protocol { supported = true break @@ -222,7 +246,8 @@ func (p *socketProvider) Socket(t *kernel.Task, stypeflags linux.SockType, proto // Conservatively ignore all flags specified by the application and add // SOCK_NONBLOCK since socketOperations requires it. - fd, err := unix.Socket(p.family, int(stype)|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, protocol) + st := int(stype) | unix.SOCK_NONBLOCK | unix.SOCK_CLOEXEC + fd, err := unix.Socket(p.family, st, protocol) if err != nil { return nil, syserr.FromError(err) } diff --git a/pkg/sentry/socket/hostinet/sockopt.go b/pkg/sentry/socket/hostinet/sockopt.go index def062af4..30b1945ff 100644 --- a/pkg/sentry/socket/hostinet/sockopt.go +++ b/pkg/sentry/socket/hostinet/sockopt.go @@ -59,6 +59,7 @@ type SockOpt struct { var SockOpts = []SockOpt{ {linux.SOL_IP, linux.IP_ADD_MEMBERSHIP, 0, false, true}, {linux.SOL_IP, linux.IP_DROP_MEMBERSHIP, 0, false, true}, + {linux.SOL_IP, linux.IP_HDRINCL, sizeofInt32, true, true}, {linux.SOL_IP, linux.IP_MULTICAST_IF, uint64(linux.SizeOfInetAddr), true, true}, {linux.SOL_IP, linux.IP_MULTICAST_LOOP, 0 /* can be 32-bit int or 8-bit uint */, true, true}, {linux.SOL_IP, linux.IP_MULTICAST_TTL, 0 /* can be 32-bit int or 8-bit uint */, true, true}, @@ -70,6 +71,7 @@ var SockOpts = []SockOpt{ {linux.SOL_IP, linux.IP_TOS, 0 /* Can be 32, 16, or 8 bits */, true, true}, {linux.SOL_IP, linux.IP_TTL, sizeofInt32, true, true}, + {linux.SOL_IPV6, linux.IPV6_CHECKSUM, sizeofInt32, true, true}, {linux.SOL_IPV6, linux.IPV6_MULTICAST_HOPS, sizeofInt32, true, true}, {linux.SOL_IPV6, linux.IPV6_RECVERR, sizeofInt32, true, true}, {linux.SOL_IPV6, linux.IPV6_RECVHOPLIMIT, sizeofInt32, true, true}, @@ -81,6 +83,7 @@ var SockOpts = []SockOpt{ {linux.SOL_IPV6, linux.IPV6_V6ONLY, sizeofInt32, true, true}, {linux.SOL_SOCKET, linux.SO_ACCEPTCONN, sizeofInt32, true, true}, + {linux.SOL_SOCKET, linux.SO_BINDTODEVICE, 0, true, true}, {linux.SOL_SOCKET, linux.SO_BROADCAST, sizeofInt32, true, true}, {linux.SOL_SOCKET, linux.SO_ERROR, sizeofInt32, true, false}, {linux.SOL_SOCKET, linux.SO_KEEPALIVE, sizeofInt32, true, true}, @@ -111,6 +114,8 @@ var SockOpts = []SockOpt{ {linux.SOL_TCP, linux.TCP_SYNCNT, sizeofInt32, true, true}, {linux.SOL_TCP, linux.TCP_USER_TIMEOUT, sizeofInt32, true, true}, {linux.SOL_TCP, linux.TCP_WINDOW_CLAMP, sizeofInt32, true, true}, + + {linux.SOL_ICMPV6, linux.ICMPV6_FILTER, uint64(linux.SizeOfICMP6Filter), true, true}, } // sockOptMap is a map of {level, name} -> SockOpts. It is an optimization for diff --git a/pkg/sentry/socket/hostinet/stack.go b/pkg/sentry/socket/hostinet/stack.go index 53288f7e6..9e854e9e3 100644 --- a/pkg/sentry/socket/hostinet/stack.go +++ b/pkg/sentry/socket/hostinet/stack.go @@ -64,6 +64,8 @@ type Stack struct { tcpSACKEnabled bool netDevFile *os.File netSNMPFile *os.File + // allowedSocketTypes is the list of allowed socket types + allowedSocketTypes []AllowedSocketType } // Destroy implements inet.Stack.Destroy. @@ -79,7 +81,7 @@ func NewStack() *Stack { } // Configure sets up the stack using the current state of the host network. -func (s *Stack) Configure() error { +func (s *Stack) Configure(allowRawSockets bool) error { if err := addHostInterfaces(s); err != nil { return err } @@ -127,6 +129,11 @@ func (s *Stack) Configure() error { s.netSNMPFile = f } + s.allowedSocketTypes = AllowedSocketTypes + if allowRawSockets { + s.allowedSocketTypes = append(s.allowedSocketTypes, AllowedRawSocketTypes...) + } + return nil } diff --git a/pkg/sentry/strace/socket.go b/pkg/sentry/strace/socket.go index b85335139..a8eded48c 100644 --- a/pkg/sentry/strace/socket.go +++ b/pkg/sentry/strace/socket.go @@ -101,6 +101,7 @@ var SocketFlagSet = abi.FlagSet{ var ipProtocol = abi.ValueSet{ linux.IPPROTO_IP: "IPPROTO_IP", linux.IPPROTO_ICMP: "IPPROTO_ICMP", + linux.IPPROTO_ICMPV6: "IPPROTO_ICMPV6", linux.IPPROTO_IGMP: "IPPROTO_IGMP", linux.IPPROTO_IPIP: "IPPROTO_IPIP", linux.IPPROTO_TCP: "IPPROTO_TCP", diff --git a/pkg/test/testutil/testutil.go b/pkg/test/testutil/testutil.go index 6b97ff185..bb8b9e823 100644 --- a/pkg/test/testutil/testutil.go +++ b/pkg/test/testutil/testutil.go @@ -49,13 +49,16 @@ import ( ) var ( - checkpoint = flag.Bool("checkpoint", BoolFromEnv("CHECKPOINT", true), "control checkpoint/restore support") - partition = flag.Int("partition", IntFromEnv("PARTITION", 1), "partition number, this is 1-indexed") - totalPartitions = flag.Int("total_partitions", IntFromEnv("TOTAL_PARTITIONS", 1), "total number of partitions") - isRunningWithHostNet = flag.Bool("hostnet", BoolFromEnv("HOSTNET", false), "whether test is running with hostnet") - runscPath = flag.String("runsc", os.Getenv("RUNTIME"), "path to runsc binary") - // Note: flag overlay is already taken by runsc. + partition = flag.Int("partition", IntFromEnv("PARTITION", 1), "partition number, this is 1-indexed") + totalPartitions = flag.Int("total_partitions", IntFromEnv("TOTAL_PARTITIONS", 1), "total number of partitions") + runscPath = flag.String("runsc", os.Getenv("RUNTIME"), "path to runsc binary") + + // Flags controlling features for sandbox under test, prefixed with + // "test-" to avoid potential conflicts with runsc flags. + checkpointSupported = flag.Bool("test-checkpoint", BoolFromEnv("TEST_CHECKPOINT", true), "control checkpoint/restore support") isRunningWithOverlay = flag.Bool("test-overlay", BoolFromEnv("TEST_OVERLAY", false), "whether test is running with --overlay2") + isRunningWithNetRaw = flag.Bool("test-net-raw", BoolFromEnv("TEST_NET_RAW", false), "whether test is running with raw socket support") + isRunningWithHostNet = flag.Bool("test-hostnet", BoolFromEnv("TEST_HOSTNET", false), "whether test is running with hostnet") // TestEnvSupportsRawSockets indicates whether a test sandbox can // create raw sockets. @@ -117,7 +120,7 @@ func DurationFromEnv(name string, def time.Duration) time.Duration { // IsCheckpointSupported returns the relevant command line flag. func IsCheckpointSupported() bool { - return *checkpoint + return *checkpointSupported } // IsRunningWithHostNet returns the relevant command line flag. @@ -125,6 +128,11 @@ func IsRunningWithHostNet() bool { return *isRunningWithHostNet } +// IsRunningWithNetRaw returns the relevant command line flag. +func IsRunningWithNetRaw() bool { + return *isRunningWithNetRaw +} + // IsRunningWithOverlay returns the relevant command line flag. func IsRunningWithOverlay() bool { return *isRunningWithOverlay diff --git a/runsc/boot/filter/extra_filters_hostinet.go b/runsc/boot/filter/extra_filters_hostinet.go index ec9f8a3a7..540dd58a6 100644 --- a/runsc/boot/filter/extra_filters_hostinet.go +++ b/runsc/boot/filter/extra_filters_hostinet.go @@ -22,7 +22,7 @@ import ( ) // hostInetFilters contains syscalls that are needed by sentry/socket/hostinet. -func hostInetFilters() seccomp.SyscallRules { +func hostInetFilters(allowRawSockets bool) seccomp.SyscallRules { rules := seccomp.SyscallRules{ unix.SYS_ACCEPT4: []seccomp.Rule{ { @@ -80,7 +80,11 @@ func hostInetFilters() seccomp.SyscallRules { // Generate rules for socket creation based on hostinet's supported // socket types. socketRules := []seccomp.Rule{} - for _, sock := range hostinet.AllowedSocketTypes { + stypes := hostinet.AllowedSocketTypes + if allowRawSockets { + stypes = append(stypes, hostinet.AllowedRawSocketTypes...) + } + for _, sock := range stypes { socketRules = append(socketRules, seccomp.Rule{ seccomp.EqualTo(sock.Family), // We always set SOCK_NONBLOCK and SOCK_CLOEXEC diff --git a/runsc/boot/filter/filter.go b/runsc/boot/filter/filter.go index c67934b6d..b7c3bfcbb 100644 --- a/runsc/boot/filter/filter.go +++ b/runsc/boot/filter/filter.go @@ -25,13 +25,14 @@ import ( // Options are seccomp filter related options. type Options struct { - Platform platform.Platform - HostNetwork bool - HostFilesystem bool - HostSocketCreate bool - HostSocketOpen bool - ProfileEnable bool - ControllerFD int + Platform platform.Platform + HostNetwork bool + HostNetworkRawSockets bool + HostFilesystem bool + HostSocketCreate bool + HostSocketOpen bool + ProfileEnable bool + ControllerFD int } // Install seccomp filters based on the given platform. @@ -44,8 +45,12 @@ func Install(opt Options) error { s.Merge(instrumentationFilters()) if opt.HostNetwork { - Report("host networking enabled: syscall filters less restrictive!") - s.Merge(hostInetFilters()) + if opt.HostNetworkRawSockets { + Report("host networking (with raw sockets) enabled: syscall filters less restrictive!") + } else { + Report("host networking enabled: syscall filters less restrictive!") + } + s.Merge(hostInetFilters(opt.HostNetworkRawSockets)) } if opt.ProfileEnable { Report("profile enabled: syscall filters less restrictive!") diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index 2e078025c..aaab3849a 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -569,14 +569,16 @@ func (l *Loader) installSeccompFilters() error { filter.Report("syscall filter is DISABLED. Running in less secure mode.") } else { hostUDS := l.root.conf.GetHostUDS() + hostnet := l.root.conf.Network == config.NetworkHost opts := filter.Options{ - Platform: l.k.Platform, - HostNetwork: l.root.conf.Network == config.NetworkHost, - HostFilesystem: l.root.conf.DirectFS, - HostSocketCreate: l.root.conf.DirectFS && hostUDS.AllowCreate(), - HostSocketOpen: l.root.conf.DirectFS && hostUDS.AllowOpen(), - ProfileEnable: l.root.conf.ProfileEnable, - ControllerFD: l.ctrl.srv.FD(), + Platform: l.k.Platform, + HostNetwork: hostnet, + HostNetworkRawSockets: hostnet && l.root.conf.EnableRaw, + HostFilesystem: l.root.conf.DirectFS, + HostSocketCreate: l.root.conf.DirectFS && hostUDS.AllowCreate(), + HostSocketOpen: l.root.conf.DirectFS && hostUDS.AllowOpen(), + ProfileEnable: l.root.conf.ProfileEnable, + ControllerFD: l.ctrl.srv.FD(), } if err := filter.Install(opts); err != nil { return fmt.Errorf("installing seccomp filters: %w", err) @@ -606,7 +608,7 @@ func (l *Loader) run() error { // is configured after the loader is created and before Run() is called. log.Debugf("Configuring host network") s := l.k.RootNetworkNamespace().Stack().(*hostinet.Stack) - if err := s.Configure(); err != nil { + if err := s.Configure(l.root.conf.EnableRaw); err != nil { return err } } diff --git a/test/e2e/exec_test.go b/test/e2e/exec_test.go index 979a64f67..39868375f 100644 --- a/test/e2e/exec_test.go +++ b/test/e2e/exec_test.go @@ -32,6 +32,7 @@ import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/bits" "gvisor.dev/gvisor/pkg/test/dockerutil" + "gvisor.dev/gvisor/pkg/test/testutil" "gvisor.dev/gvisor/runsc/specutils" ) @@ -77,15 +78,24 @@ func TestExecPrivileged(t *testing.T) { d := dockerutil.MakeContainer(ctx, t) defer d.CleanUp(ctx) - // Start the container with all capabilities dropped. - if err := d.Spawn(ctx, dockerutil.RunOpts{ + // Container will drop all capabilities. + opts := dockerutil.RunOpts{ Image: "basic/alpine", CapDrop: []string{"all"}, - }, "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil { + } + + // But if we are running with host network stack and raw sockets, then + // we require CAP_NET_RAW, so add that back. + if testutil.IsRunningWithHostNet() && testutil.IsRunningWithNetRaw() { + opts.CapAdd = []string{"NET_RAW"} + } + + // Start the container. + if err := d.Spawn(ctx, opts, "sh", "-c", "cat /proc/self/status; sleep 100"); err != nil { t.Fatalf("docker run failed: %v", err) } - // Check that all capabilities where dropped from container. + // Grab the capabilities from inside container. matches, err := d.WaitForOutputSubmatch(ctx, "CapEff:\t([0-9a-f]+)\n", 5*time.Second) if err != nil { t.Fatalf("WaitForOutputSubmatch() timeout: %v", err) @@ -98,12 +108,18 @@ func TestExecPrivileged(t *testing.T) { t.Fatalf("failed to convert capabilities %q: %v", matches[1], err) } t.Logf("Container capabilities: %#x", containerCaps) - if containerCaps != 0 { - t.Fatalf("Container should have no capabilities: %x", containerCaps) + + // Expect no capabilities, unless raw sockets configured. + var wantContainerCaps uint64 + if testutil.IsRunningWithNetRaw() { + wantContainerCaps |= bits.MaskOf64(int(linux.CAP_NET_RAW)) + } + if containerCaps != wantContainerCaps { + t.Fatalf("Container caps got %x want %x", containerCaps, wantContainerCaps) } - // Check that 'exec --privileged' adds all capabilities, except for - // CAP_NET_RAW. + // Check that 'exec --privileged' adds all capabilities except + // CAP_NET_RAW, unless raw sockets configured. got, err := d.Exec(ctx, dockerutil.ExecOpts{ Privileged: true, }, "grep", "CapEff:", "/proc/self/status") @@ -111,7 +127,10 @@ func TestExecPrivileged(t *testing.T) { t.Fatalf("docker exec failed: %v", err) } t.Logf("Exec CapEff: %v", got) - wantCaps := specutils.AllCapabilitiesUint64() &^ bits.MaskOf64(int(linux.CAP_NET_RAW)) + wantCaps := specutils.AllCapabilitiesUint64() + if !testutil.IsRunningWithNetRaw() { + wantCaps &= ^bits.MaskOf64(int(linux.CAP_NET_RAW)) + } wantStr := fmt.Sprintf("CapEff:\t%016x\n", wantCaps) if got == wantStr { // All good. diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go index a56b1cc5b..d506f467b 100644 --- a/test/e2e/integration_test.go +++ b/test/e2e/integration_test.go @@ -568,12 +568,6 @@ func TestLink(t *testing.T) { // This test ensures we can run ping without errors. func TestPing4Loopback(t *testing.T) { - if testutil.IsRunningWithHostNet() { - // TODO(gvisor.dev/issue/5011): support ICMP sockets in hostnet and enable - // this test. - t.Skip("hostnet only supports TCP/UDP sockets, so ping is not supported.") - } - runIntegrationTest(t, nil, "./ping4.sh") } diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index a029b45db..390715a27 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -447,6 +447,7 @@ syscall_test( syscall_test( size = "medium", + add_hostinet = True, # Takes too long under gotsan to run. tags = ["nogotsan"], test = "//test/syscalls/linux:ping_socket_test", @@ -557,14 +558,17 @@ syscall_test( ) syscall_test( + add_hostinet = True, test = "//test/syscalls/linux:raw_socket_hdrincl_test", ) syscall_test( + add_hostinet = True, test = "//test/syscalls/linux:raw_socket_icmp_test", ) syscall_test( + add_hostinet = True, shard_count = more_shards, test = "//test/syscalls/linux:raw_socket_test", ) diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc index 2ab90cf25..a21c5625b 100644 --- a/test/syscalls/linux/raw_socket.cc +++ b/test/syscalls/linux/raw_socket.cc @@ -797,7 +797,7 @@ TEST_P(RawSocketTest, RecvBufLimits) { ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); int sent = 4; - if (IsRunningOnGvisor()) { + if (IsRunningOnGvisor() && !IsRunningWithHostinet()) { // Linux seems to drop the 4th packet even though technically it should // fit in the receive buffer. ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); @@ -1451,7 +1451,7 @@ TEST(RawSocketTest, SetIPv6ChecksumError_ReadShort) { ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_RAW, IPPROTO_UDP)); int intV = 2; - if (IsRunningOnGvisor() && !IsRunningWithHostinet()) { + if (IsRunningOnGvisor()) { // TODO(https://gvisor.dev/issue/6982): This is a deviation from Linux. We // should determine if we want to match the behaviour or handle the error // more gracefully. diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc index 8c8daf5d2..75662b4df 100644 --- a/test/syscalls/linux/raw_socket_icmp.cc +++ b/test/syscalls/linux/raw_socket_icmp.cc @@ -244,7 +244,12 @@ 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(HaveRawIPSocketCapability())); + // By default, ping sockets cannot be created on Linux, even with root privs. + // So we only run the test with gVisor and not hostinet, and even then require + // CAP_NET_RAW. + // See https://lwn.net/Articles/443051/ + SKIP_IF(!IsRunningOnGvisor() || IsRunningWithHostinet() || + ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); FileDescriptor ping_sock = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); @@ -294,7 +299,12 @@ 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(HaveRawIPSocketCapability())); + // By default, ping sockets cannot be created on Linux, even with root privs. + // So we only run the test with gVisor and not hostinet, and even then require + // CAP_NET_RAW. + // See https://lwn.net/Articles/443051/ + SKIP_IF(!IsRunningOnGvisor() || IsRunningWithHostinet() || + ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); FileDescriptor ping_sock = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); @@ -335,7 +345,12 @@ 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(HaveRawIPSocketCapability())); + // By default, ping sockets cannot be created on Linux, even with root privs. + // So we only run the test with gVisor and not hostinet, and even then require + // CAP_NET_RAW. + // See https://lwn.net/Articles/443051/ + SKIP_IF(!IsRunningOnGvisor() || IsRunningWithHostinet() || + ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())); FileDescriptor ping_sock = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));