diff options
author | Ian Gudger <igudger@google.com> | 2018-12-26 23:51:00 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2018-12-26 23:52:12 -0800 |
commit | bce2f9751f415da869d04ccb53833b024373666d (patch) | |
tree | cc9569b865762b171e183225cf2813de0a3dd046 | |
parent | bfa2f314ca05854b0d08aa2f5c2b93b16542d95f (diff) |
Plumb IP_MULTICAST_TTL to netstack.
PiperOrigin-RevId: 226993086
Change-Id: I71757f231436538081d494da32ca69f709bc71c7
-rw-r--r-- | pkg/sentry/socket/epsocket/epsocket.go | 113 | ||||
-rw-r--r-- | test/syscalls/linux/BUILD | 19 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_udp_generic.cc | 121 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_udp_generic.h | 29 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ip_udp_loopback.cc | 5 |
5 files changed, 276 insertions, 11 deletions
diff --git a/pkg/sentry/socket/epsocket/epsocket.go b/pkg/sentry/socket/epsocket/epsocket.go index 283f1839d..1b9c75949 100644 --- a/pkg/sentry/socket/epsocket/epsocket.go +++ b/pkg/sentry/socket/epsocket/epsocket.go @@ -531,8 +531,10 @@ func GetSockOpt(t *kernel.Task, s socket.Socket, ep commonEndpoint, family int, case linux.SOL_IPV6: return getSockOptIPv6(t, ep, name, outLen) - case linux.SOL_IP, - linux.SOL_UDP, + case linux.SOL_IP: + return getSockOptIP(t, ep, name, outLen) + + case linux.SOL_UDP, linux.SOL_ICMPV6, linux.SOL_RAW, linux.SOL_PACKET: @@ -787,7 +789,7 @@ func getSockOptTCP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfa t.Kernel().EmitUnimplementedEvent(t) default: - emitUmplementedEventTCP(t, name) + emitUnimplementedEventTCP(t, name) } return nil, syserr.ErrProtocolNotAvailable } @@ -811,7 +813,28 @@ func getSockOptIPv6(t *kernel.Task, ep commonEndpoint, name, outLen int) (interf t.Kernel().EmitUnimplementedEvent(t) default: - emitUmplementedEventIPv6(t, name) + emitUnimplementedEventIPv6(t, name) + } + return nil, syserr.ErrProtocolNotAvailable +} + +// getSockOptIP implements GetSockOpt when level is SOL_IP. +func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interface{}, *syserr.Error) { + switch name { + case linux.IP_MULTICAST_TTL: + if outLen < sizeOfInt32 { + return nil, syserr.ErrInvalidArgument + } + + var v tcpip.MulticastTTLOption + if err := ep.GetSockOpt(&v); err != nil { + return nil, syserr.TranslateNetstackError(err) + } + + return int32(v), nil + + default: + emitUnimplementedEventIP(t, name) } return nil, syserr.ErrProtocolNotAvailable } @@ -992,7 +1015,7 @@ func setSockOptTCP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) * t.Kernel().EmitUnimplementedEvent(t) default: - emitUmplementedEventTCP(t, name) + emitUnimplementedEventTCP(t, name) } // Default to the old behavior; hand off to network stack. @@ -1028,7 +1051,7 @@ func setSockOptIPv6(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) t.Kernel().EmitUnimplementedEvent(t) default: - emitUmplementedEventIPv6(t, name) + emitUnimplementedEventIPv6(t, name) } // Default to the old behavior; hand off to network stack. @@ -1038,6 +1061,21 @@ func setSockOptIPv6(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) // setSockOptIP implements SetSockOpt when level is SOL_IP. func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error { switch name { + case linux.IP_MULTICAST_TTL: + if len(optVal) < sizeOfInt32 { + return syserr.ErrInvalidArgument + } + + v := int32(usermem.ByteOrder.Uint32(optVal)) + if v == -1 { + // Linux translates -1 to 1. + v = 1 + } + if v < 0 || v > 255 { + return syserr.ErrInvalidArgument + } + return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.MulticastTTLOption(v))) + case linux.IP_ADD_MEMBERSHIP, linux.MCAST_JOIN_GROUP, linux.IP_MULTICAST_IF: // FIXME: Disallow IP-level multicast group options by // default. These will need to be supported by appropriately plumbing @@ -1060,7 +1098,6 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s linux.IP_MTU_DISCOVER, linux.IP_MULTICAST_ALL, linux.IP_MULTICAST_LOOP, - linux.IP_MULTICAST_TTL, linux.IP_NODEFRAG, linux.IP_OPTIONS, linux.IP_PASSSEC, @@ -1092,10 +1129,10 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{})) } -// emitUmplementedEventTCP emits unimplemented event if name is valid. This +// emitUnimplementedEventTCP emits unimplemented event if name is valid. This // function contains names that are common between Get and SetSockOpt when // level is SOL_TCP. -func emitUmplementedEventTCP(t *kernel.Task, name int) { +func emitUnimplementedEventTCP(t *kernel.Task, name int) { switch name { case linux.TCP_CONGESTION, linux.TCP_CORK, @@ -1129,10 +1166,10 @@ func emitUmplementedEventTCP(t *kernel.Task, name int) { } } -// emitUmplementedEventIPv6 emits unimplemented event if name is valid. It +// emitUnimplementedEventIPv6 emits unimplemented event if name is valid. It // contains names that are common between Get and SetSockOpt when level is // SOL_IPV6. -func emitUmplementedEventIPv6(t *kernel.Task, name int) { +func emitUnimplementedEventIPv6(t *kernel.Task, name int) { switch name { case linux.IPV6_2292DSTOPTS, linux.IPV6_2292HOPLIMIT, @@ -1179,6 +1216,60 @@ func emitUmplementedEventIPv6(t *kernel.Task, name int) { } } +// emitUnimplementedEventIP emits unimplemented event if name is valid. It +// contains names that are common between Get and SetSockOpt when level is +// SOL_IP. +func emitUnimplementedEventIP(t *kernel.Task, name int) { + switch name { + case linux.IP_TOS, + linux.IP_TTL, + linux.IP_HDRINCL, + linux.IP_OPTIONS, + linux.IP_ROUTER_ALERT, + linux.IP_RECVOPTS, + linux.IP_RETOPTS, + linux.IP_PKTINFO, + linux.IP_PKTOPTIONS, + linux.IP_MTU_DISCOVER, + linux.IP_RECVERR, + linux.IP_RECVTTL, + linux.IP_RECVTOS, + linux.IP_MTU, + linux.IP_FREEBIND, + linux.IP_IPSEC_POLICY, + linux.IP_XFRM_POLICY, + linux.IP_PASSSEC, + linux.IP_TRANSPARENT, + linux.IP_ORIGDSTADDR, + linux.IP_MINTTL, + linux.IP_NODEFRAG, + linux.IP_CHECKSUM, + linux.IP_BIND_ADDRESS_NO_PORT, + linux.IP_RECVFRAGSIZE, + linux.IP_MULTICAST_IF, + linux.IP_MULTICAST_TTL, + linux.IP_MULTICAST_LOOP, + linux.IP_ADD_MEMBERSHIP, + linux.IP_DROP_MEMBERSHIP, + linux.IP_UNBLOCK_SOURCE, + linux.IP_BLOCK_SOURCE, + linux.IP_ADD_SOURCE_MEMBERSHIP, + linux.IP_DROP_SOURCE_MEMBERSHIP, + linux.IP_MSFILTER, + linux.MCAST_JOIN_GROUP, + linux.MCAST_BLOCK_SOURCE, + linux.MCAST_UNBLOCK_SOURCE, + linux.MCAST_LEAVE_GROUP, + linux.MCAST_JOIN_SOURCE_GROUP, + linux.MCAST_LEAVE_SOURCE_GROUP, + linux.MCAST_MSFILTER, + linux.IP_MULTICAST_ALL, + linux.IP_UNICAST_IF: + + t.Kernel().EmitUnimplementedEvent(t) + } +} + // isLinkLocal determines if the given IPv6 address is link-local. This is the // case when it has the fe80::/10 prefix. This check is used to determine when // the NICID is relevant for a given IPv6 address. diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 00d521400..0f9b406d8 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1875,6 +1875,24 @@ cc_library( alwayslink = 1, ) +cc_library( + name = "socket_ip_udp_test_cases", + testonly = 1, + srcs = [ + "socket_ip_udp_generic.cc", + ], + hdrs = [ + "socket_ip_udp_generic.h", + ], + deps = [ + ":ip_socket_test_util", + ":socket_test_util", + "//test/util:test_util", + "@com_google_googletest//:gtest", + ], + alwayslink = 1, +) + cc_binary( name = "socket_abstract_test", testonly = 1, @@ -2044,6 +2062,7 @@ cc_binary( deps = [ ":ip_socket_test_util", ":socket_generic_test_cases", + ":socket_ip_udp_test_cases", ":socket_non_stream_test_cases", ":socket_test_util", "//test/util:test_main", diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc new file mode 100644 index 000000000..789154fb3 --- /dev/null +++ b/test/syscalls/linux/socket_ip_udp_generic.cc @@ -0,0 +1,121 @@ +// Copyright 2018 Google LLC +// +// 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_generic.h" + +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "test/syscalls/linux/socket_test_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +TEST_P(UDPSocketPairTest, MulticastTTLDefault) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, 1); +} + +TEST_P(UDPSocketPairTest, SetUDPMulticastTTLMin) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + constexpr int kMin = 0; + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &kMin, sizeof(kMin)), + SyscallSucceeds()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kMin); +} + +TEST_P(UDPSocketPairTest, SetUDPMulticastTTLMax) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + constexpr int kMax = 255; + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &kMax, sizeof(kMax)), + SyscallSucceeds()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, kMax); +} + +TEST_P(UDPSocketPairTest, SetUDPMulticastTTLNegativeOne) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + constexpr int kArbitrary = 6; + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &kArbitrary, sizeof(kArbitrary)), + SyscallSucceeds()); + + constexpr int kNegOne = -1; + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &kNegOne, sizeof(kNegOne)), + SyscallSucceeds()); + + int get = -1; + socklen_t get_len = sizeof(get); + EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &get, &get_len), + SyscallSucceedsWithValue(0)); + EXPECT_EQ(get_len, sizeof(get)); + EXPECT_EQ(get, 1); +} + +TEST_P(UDPSocketPairTest, SetUDPMulticastTTLBelowMin) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + constexpr int kBelowMin = -2; + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &kBelowMin, sizeof(kBelowMin)), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(UDPSocketPairTest, SetUDPMulticastTTLAboveMax) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + constexpr int kAboveMax = 256; + EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, + &kAboveMax, sizeof(kAboveMax)), + SyscallFailsWithErrno(EINVAL)); +} + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_generic.h b/test/syscalls/linux/socket_ip_udp_generic.h new file mode 100644 index 000000000..8b8fc7c6e --- /dev/null +++ b/test/syscalls/linux/socket_ip_udp_generic.h @@ -0,0 +1,29 @@ +// Copyright 2018 Google LLC +// +// 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_IP_UDP_GENERIC_H_ +#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_GENERIC_H_ + +#include "test/syscalls/linux/socket_test_util.h" + +namespace gvisor { +namespace testing { + +// Test fixture for tests that apply to pairs of connected UDP sockets. +using UDPSocketPairTest = SocketPairTest; + +} // namespace testing +} // namespace gvisor + +#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_GENERIC_H_ diff --git a/test/syscalls/linux/socket_ip_udp_loopback.cc b/test/syscalls/linux/socket_ip_udp_loopback.cc index 8a98fa8df..f3548469f 100644 --- a/test/syscalls/linux/socket_ip_udp_loopback.cc +++ b/test/syscalls/linux/socket_ip_udp_loopback.cc @@ -16,6 +16,7 @@ #include "test/syscalls/linux/ip_socket_test_util.h" #include "test/syscalls/linux/socket_generic.h" +#include "test/syscalls/linux/socket_ip_udp_generic.h" #include "test/syscalls/linux/socket_non_stream.h" #include "test/syscalls/linux/socket_test_util.h" #include "test/util/test_util.h" @@ -44,5 +45,9 @@ INSTANTIATE_TEST_CASE_P( AllUnixDomainSockets, NonStreamSocketPairTest, ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); +INSTANTIATE_TEST_CASE_P( + UDPSockets, UDPSocketPairTest, + ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); + } // namespace testing } // namespace gvisor |