From b28bd31bb6d920c23b5036d94bc7123143369e24 Mon Sep 17 00:00:00 2001 From: Ghanan Gowripalan Date: Thu, 16 Sep 2021 20:04:00 -0700 Subject: [PATCH] Allow creating packet socket bound to any protocol ...even protocols the stack is unaware of. While I am here, annotate checklocks on stack.packetEndpointList. PiperOrigin-RevId: 397226754 --- pkg/tcpip/stack/nic.go | 20 ++++++---- test/syscalls/BUILD | 4 ++ test/syscalls/linux/BUILD | 15 +++++++ test/syscalls/linux/packet_socket.cc | 60 ++++++++++++++++++++++++++++ test/util/capability_util.h | 2 + 5 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 test/syscalls/linux/packet_socket.cc diff --git a/pkg/tcpip/stack/nic.go b/pkg/tcpip/stack/nic.go index ab7e1d859..29d580e76 100644 --- a/pkg/tcpip/stack/nic.go +++ b/pkg/tcpip/stack/nic.go @@ -97,6 +97,8 @@ type packetEndpointList struct { mu sync.RWMutex // eps is protected by mu, but the contained PacketEndpoint values are not. + // + // +checklocks:mu eps []PacketEndpoint } @@ -117,6 +119,12 @@ func (p *packetEndpointList) remove(ep PacketEndpoint) { } } +func (p *packetEndpointList) len() int { + p.mu.RLock() + defer p.mu.RUnlock() + return len(p.eps) +} + // forEach calls fn with each endpoints in p while holding the read lock on p. func (p *packetEndpointList) forEach(fn func(PacketEndpoint)) { p.mu.RLock() @@ -157,14 +165,8 @@ func newNIC(stack *Stack, id tcpip.NICID, name string, ep LinkEndpoint, ctx NICC resolutionRequired := ep.Capabilities()&CapabilityResolutionRequired != 0 - // Register supported packet and network endpoint protocols. - for _, netProto := range header.Ethertypes { - nic.packetEPs.eps[netProto] = new(packetEndpointList) - } for _, netProto := range stack.networkProtocols { netNum := netProto.Number() - nic.packetEPs.eps[netNum] = new(packetEndpointList) - netEP := netProto.NewEndpoint(nic, nic) nic.networkEndpoints[netNum] = netEP @@ -974,7 +976,8 @@ func (n *nic) registerPacketEndpoint(netProto tcpip.NetworkProtocolNumber, ep Pa eps, ok := n.packetEPs.eps[netProto] if !ok { - return &tcpip.ErrNotSupported{} + eps = new(packetEndpointList) + n.packetEPs.eps[netProto] = eps } eps.add(ep) @@ -990,6 +993,9 @@ func (n *nic) unregisterPacketEndpoint(netProto tcpip.NetworkProtocolNumber, ep return } eps.remove(ep) + if eps.len() == 0 { + delete(n.packetEPs.eps, netProto) + } } // isValidForOutgoing returns true if the endpoint can be used to send out a diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index b66112f6e..8494862fa 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -366,6 +366,10 @@ syscall_test( test = "//test/syscalls/linux:packet_socket_raw_test", ) +syscall_test( + test = "//test/syscalls/linux:packet_socket_test", +) + syscall_test( test = "//test/syscalls/linux:partial_bad_buffer_test", ) diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 1fcf78ddf..85fa58970 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -7,6 +7,7 @@ package( exports_files( [ + "packet_socket.cc", "packet_socket_dgram.cc", "packet_socket_raw.cc", "raw_socket.cc", @@ -1480,6 +1481,20 @@ cc_binary( ], ) +cc_binary( + name = "packet_socket_test", + testonly = 1, + srcs = ["packet_socket.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:file_descriptor", + "//test/util:socket_util", + gtest, + "//test/util:test_main", + ], +) + cc_binary( name = "pty_test", testonly = 1, diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc new file mode 100644 index 000000000..c8d1e1d4a --- /dev/null +++ b/test/syscalls/linux/packet_socket.cc @@ -0,0 +1,60 @@ +// 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 +#include + +#include + +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/socket_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +using ::testing::Combine; +using ::testing::Values; + +class PacketSocketTest : public ::testing::TestWithParam> { + protected: + 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"; + } + } +}; + +TEST_P(PacketSocketTest, Create) { + const auto [type, protocol] = GetParam(); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, type, htons(protocol))); + EXPECT_GE(fd.get(), 0); +} + +INSTANTIATE_TEST_SUITE_P(AllPacketSocketTests, PacketSocketTest, + Combine(Values(SOCK_DGRAM, SOCK_RAW), + Values(0, 1, 255, ETH_P_IP, ETH_P_IPV6, + std::numeric_limits::max()))); + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/util/capability_util.h b/test/util/capability_util.h index 318a43feb..ac1a1b32b 100644 --- a/test/util/capability_util.h +++ b/test/util/capability_util.h @@ -17,6 +17,8 @@ #ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ #define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ +#include "test/util/posix_error.h" + #if defined(__Fuchsia__) // Nothing to include. #elif defined(__linux__)