summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGhanan Gowripalan <ghanan@google.com>2021-09-16 20:04:00 -0700
committergVisor bot <gvisor-bot@google.com>2021-09-16 20:06:26 -0700
commitb28bd31bb6d920c23b5036d94bc7123143369e24 (patch)
tree5994e0cc0da688498cf6639816ed33ed9068fa7e
parent85bd3dd9b119cbc5a68b778a18ae73d3567ff610 (diff)
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
-rw-r--r--pkg/tcpip/stack/nic.go20
-rw-r--r--test/syscalls/BUILD4
-rw-r--r--test/syscalls/linux/BUILD15
-rw-r--r--test/syscalls/linux/packet_socket.cc60
-rw-r--r--test/util/capability_util.h2
5 files changed, 94 insertions, 7 deletions
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
@@ -367,6 +367,10 @@ syscall_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",
@@ -1481,6 +1482,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,
srcs = ["pty.cc"],
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 <net/if.h>
+#include <netinet/if_ether.h>
+
+#include <limits>
+
+#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<std::tuple<int, int>> {
+ 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<uint16_t>::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__)