Plumb IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP to netstack.

Also includes a few fixes for IPv4 multicast support. IPv6 support is coming in
a followup CL.

PiperOrigin-RevId: 233008638
Change-Id: If7dae6222fef43fda48033f0292af77832d95e82
This commit is contained in:
Ian Gudger
2019-02-07 23:15:23 -08:00
committed by Shentubot
parent fda4d1f4f1
commit 80f901b16b
15 changed files with 695 additions and 24 deletions
+18 -3
View File
@@ -204,15 +204,30 @@ const (
// uapi/linux/socket.h.
const SockAddrMax = 128
// SockAddrInt is struct sockaddr_in, from uapi/linux/in.h.
// InetAddr is struct in_addr, from uapi/linux/in.h.
type InetAddr [4]byte
// SockAddrInet is struct sockaddr_in, from uapi/linux/in.h.
type SockAddrInet struct {
Family uint16
Port uint16
Addr [4]byte
Addr InetAddr
Zero [8]uint8 // pad to sizeof(struct sockaddr).
}
// SockAddrInt6 is struct sockaddr_in6, from uapi/linux/in6.h.
// InetMulticastRequest is struct ip_mreq, from uapi/linux/in.h.
type InetMulticastRequest struct {
MulticastAddr InetAddr
InterfaceAddr InetAddr
}
// InetMulticastRequestWithNIC is struct ip_mreqn, from uapi/linux/in.h.
type InetMulticastRequestWithNIC struct {
InetMulticastRequest
InterfaceIndex int32
}
// SockAddrInet6 is struct sockaddr_in6, from uapi/linux/in6.h.
type SockAddrInet6 struct {
Family uint16
Port uint16
+44 -2
View File
@@ -1078,6 +1078,25 @@ func setSockOptIPv6(t *kernel.Task, ep commonEndpoint, name int, optVal []byte)
return syserr.TranslateNetstackError(ep.SetSockOpt(struct{}{}))
}
var (
inetMulticastRequestSize = int(binary.Size(linux.InetMulticastRequest{}))
inetMulticastRequestWithNICSize = int(binary.Size(linux.InetMulticastRequestWithNIC{}))
)
func copyInMulticastRequest(optVal []byte) (linux.InetMulticastRequestWithNIC, *syserr.Error) {
if len(optVal) < inetMulticastRequestSize {
return linux.InetMulticastRequestWithNIC{}, syserr.ErrInvalidArgument
}
var req linux.InetMulticastRequestWithNIC
if len(optVal) >= inetMulticastRequestWithNICSize {
binary.Unmarshal(optVal[:inetMulticastRequestWithNICSize], usermem.ByteOrder, &req)
} else {
binary.Unmarshal(optVal[:inetMulticastRequestSize], usermem.ByteOrder, &req.InetMulticastRequest)
}
return req, nil
}
// setSockOptIP implements SetSockOpt when level is SOL_IP.
func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error {
switch name {
@@ -1096,7 +1115,31 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
}
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.MulticastTTLOption(v)))
case linux.IP_ADD_MEMBERSHIP, linux.MCAST_JOIN_GROUP, linux.IP_MULTICAST_IF:
case linux.IP_ADD_MEMBERSHIP:
req, err := copyInMulticastRequest(optVal)
if err != nil {
return err
}
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.AddMembershipOption{
NIC: tcpip.NICID(req.InterfaceIndex),
InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]),
MulticastAddr: tcpip.Address(req.MulticastAddr[:]),
}))
case linux.IP_DROP_MEMBERSHIP:
req, err := copyInMulticastRequest(optVal)
if err != nil {
return err
}
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.RemoveMembershipOption{
NIC: tcpip.NICID(req.InterfaceIndex),
InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]),
MulticastAddr: tcpip.Address(req.MulticastAddr[:]),
}))
case 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
// the level through to the network stack (if at all). However, we
@@ -1108,7 +1151,6 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
linux.IP_BIND_ADDRESS_NO_PORT,
linux.IP_BLOCK_SOURCE,
linux.IP_CHECKSUM,
linux.IP_DROP_MEMBERSHIP,
linux.IP_DROP_SOURCE_MEMBERSHIP,
linux.IP_FREEBIND,
linux.IP_HDRINCL,
+7
View File
@@ -742,6 +742,9 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
return Route{}, tcpip.ErrNoRoute
}
// TODO: Route multicast packets with no specified local
// address or NIC.
for i := range s.routeTable {
if (id != 0 && id != s.routeTable[i].NIC) || (len(remoteAddr) != 0 && !s.routeTable[i].Match(remoteAddr)) {
continue
@@ -768,6 +771,10 @@ func (s *Stack) FindRoute(id tcpip.NICID, localAddr, remoteAddr tcpip.Address, n
return r, nil
}
if isMulticast {
return Route{}, tcpip.ErrNetworkUnreachable
}
return Route{}, tcpip.ErrNoRoute
}
+11 -5
View File
@@ -99,6 +99,7 @@ type endpoint struct {
effectiveNetProtos []tcpip.NetworkProtocolNumber
}
// +stateify savable
type multicastMembership struct {
nicID tcpip.NICID
multicastAddr tcpip.Address
@@ -412,6 +413,8 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
}
if nicID == 0 {
// TODO: Allow adding memberships without
// specifing an interface.
return tcpip.ErrNoRoute
}
@@ -766,9 +769,11 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error
}
}
nicid := addr.NIC
if len(addr.Addr) != 0 {
// A local address was specified, verify that it's valid.
if e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr) == 0 {
nicid = e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr)
if nicid == 0 {
return tcpip.ErrBadLocalAddress
}
}
@@ -777,21 +782,21 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress, commit func() *tcpip.Error
LocalPort: addr.Port,
LocalAddress: addr.Addr,
}
id, err = e.registerWithStack(addr.NIC, netProtos, id)
id, err = e.registerWithStack(nicid, netProtos, id)
if err != nil {
return err
}
if commit != nil {
if err := commit(); err != nil {
// Unregister, the commit failed.
e.stack.UnregisterTransportEndpoint(addr.NIC, netProtos, ProtocolNumber, id, e)
e.stack.UnregisterTransportEndpoint(nicid, netProtos, ProtocolNumber, id, e)
e.stack.ReleasePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort)
return err
}
}
e.id = id
e.regNICID = addr.NIC
e.regNICID = nicid
e.effectiveNetProtos = netProtos
// Mark endpoint as bound.
@@ -815,7 +820,8 @@ func (e *endpoint) Bind(addr tcpip.FullAddress, commit func() *tcpip.Error) *tcp
return err
}
e.bindNICID = addr.NIC
// Save the effective NICID generated by bindLocked.
e.bindNICID = e.regNICID
return nil
}
@@ -103,4 +103,10 @@ func (e *endpoint) afterLoad() {
if err != nil {
panic(*err)
}
for _, m := range e.multicastMemberships {
if err := e.stack.JoinGroup(e.netProto, m.nicID, m.multicastAddr); err != nil {
panic(err)
}
}
}
+2
View File
@@ -361,6 +361,8 @@ syscall_test(
test = "//test/syscalls/linux:socket_ip_udp_loopback_test",
)
syscall_test(test = "//test/syscalls/linux:socket_ipv4_udp_unbound_loopback_test")
syscall_test(test = "//test/syscalls/linux:socket_netdevice_test")
syscall_test(test = "//test/syscalls/linux:socket_netlink_route_test")
+34
View File
@@ -1931,6 +1931,24 @@ cc_library(
alwayslink = 1,
)
cc_library(
name = "socket_ipv4_udp_unbound_test_cases",
testonly = 1,
srcs = [
"socket_ipv4_udp_unbound.cc",
],
hdrs = [
"socket_ipv4_udp_unbound.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,
@@ -2124,6 +2142,22 @@ cc_binary(
],
)
cc_binary(
name = "socket_ipv4_udp_unbound_loopback_test",
testonly = 1,
srcs = [
"socket_ipv4_udp_unbound_loopback.cc",
],
linkstatic = 1,
deps = [
":ip_socket_test_util",
":socket_ipv4_udp_unbound_test_cases",
":socket_test_util",
"//test/util:test_main",
"//test/util:test_util",
],
)
cc_binary(
name = "socket_domain_test",
testonly = 1,
+27 -6
View File
@@ -12,11 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <net/if.h>
#include <sys/ioctl.h>
#include <cstring>
#include "test/syscalls/linux/ip_socket_test_util.h"
namespace gvisor {
namespace testing {
PosixErrorOr<int> InterfaceIndex(std::string name) {
// TODO: Consider using netlink.
ifreq req = {};
memcpy(req.ifr_name, name.c_str(), name.size());
ASSIGN_OR_RETURN_ERRNO(auto sock, Socket(AF_INET, SOCK_DGRAM, 0));
RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(sock.get(), SIOCGIFINDEX, &req));
return req.ifr_ifindex;
}
namespace {
std::string DescribeSocketType(int type) {
@@ -28,7 +41,7 @@ std::string DescribeSocketType(int type) {
SocketPairKind IPv6TCPAcceptBindSocketPair(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv6 TCP socket");
absl::StrCat(DescribeSocketType(type), "connected IPv6 TCP socket");
return SocketPairKind{
description, TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM,
0, /* dual_stack = */ false)};
@@ -36,7 +49,7 @@ SocketPairKind IPv6TCPAcceptBindSocketPair(int type) {
SocketPairKind IPv4TCPAcceptBindSocketPair(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv4 TCP socket");
absl::StrCat(DescribeSocketType(type), "connected IPv4 TCP socket");
return SocketPairKind{
description, TCPAcceptBindSocketPairCreator(AF_INET, type | SOCK_STREAM,
0, /* dual_stack = */ false)};
@@ -44,7 +57,7 @@ SocketPairKind IPv4TCPAcceptBindSocketPair(int type) {
SocketPairKind DualStackTCPAcceptBindSocketPair(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "dual stack TCP socket");
absl::StrCat(DescribeSocketType(type), "connected dual stack TCP socket");
return SocketPairKind{
description, TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM,
0, /* dual_stack = */ true)};
@@ -52,7 +65,7 @@ SocketPairKind DualStackTCPAcceptBindSocketPair(int type) {
SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv6 UDP socket");
absl::StrCat(DescribeSocketType(type), "connected IPv6 UDP socket");
return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator(
AF_INET6, type | SOCK_DGRAM, 0,
/* dual_stack = */ false)};
@@ -60,7 +73,7 @@ SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type) {
SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket");
absl::StrCat(DescribeSocketType(type), "connected IPv4 UDP socket");
return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator(
AF_INET, type | SOCK_DGRAM, 0,
/* dual_stack = */ false)};
@@ -68,11 +81,19 @@ SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type) {
SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "dual stack UDP socket");
absl::StrCat(DescribeSocketType(type), "connected dual stack UDP socket");
return SocketPairKind{description, UDPBidirectionalBindSocketPairCreator(
AF_INET6, type | SOCK_DGRAM, 0,
/* dual_stack = */ true)};
}
SocketPairKind IPv4UDPUnboundSocketPair(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket");
return SocketPairKind{
description, UDPUnboundSocketPairCreator(AF_INET, type | SOCK_DGRAM, 0,
/* dual_stack = */ false)};
}
} // namespace testing
} // namespace gvisor
@@ -21,6 +21,9 @@
namespace gvisor {
namespace testing {
// InterfaceIndex returns the index of the named interface.
PosixErrorOr<int> InterfaceIndex(std::string name);
// 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.
@@ -51,6 +54,10 @@ SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type);
// AF_INET6 and the given type bound to the IPv4 loopback.
SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type);
// IPv4UDPUnboundSocketPair returns a SocketPairKind that represents
// SocketPairs created with AF_INET and the given type.
SocketPairKind IPv4UDPUnboundSocketPair(int type);
} // namespace testing
} // namespace gvisor
@@ -117,5 +117,19 @@ TEST_P(UDPSocketPairTest, SetUDPMulticastTTLAboveMax) {
SyscallFailsWithErrno(EINVAL));
}
TEST_P(UDPSocketPairTest, SetEmptyIPAddMembership) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct ip_mreqn req = {};
int ret = setsockopt(sockets->first_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &req,
sizeof(req));
// FIXME: gVisor returns the incorrect errno.
if (IsRunningOnGvisor()) {
EXPECT_THAT(ret, SyscallFails());
} else {
EXPECT_THAT(ret, SyscallFailsWithErrno(EINVAL));
}
}
} // namespace testing
} // namespace gvisor
@@ -0,0 +1,424 @@
// Copyright 2019 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_ipv4_udp_unbound.h"
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <cstdio>
#include "gtest/gtest.h"
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
// Check that packets are not received without a group memebership. Default send
// interface configured by bind.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNoGroup) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Bind the first FD to the loopback. This is an alternative to
// IP_MULTICAST_IF for setting the default send interface.
sockaddr_in senderAddr = {};
senderAddr.sin_family = AF_INET;
senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(
bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr),
sizeof(senderAddr)),
SyscallSucceeds());
// Bind the second FD to the v4 any address. If multicast worked like unicast,
// this would ensure that we get the packet.
sockaddr_in receiverAddr = {};
receiverAddr.sin_family = AF_INET;
receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
EXPECT_THAT(
bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr),
sizeof(receiverAddr)),
SyscallSucceeds());
socklen_t receiverAddrLen = sizeof(receiverAddr);
EXPECT_THAT(
getsockname(sockets->second_fd(),
reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen),
SyscallSucceeds());
EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr));
// Send the multicast packet.
sockaddr_in sendAddr = {};
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = receiverAddr.sin_port;
sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1");
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
EXPECT_THAT(RetryEINTR(sendto)(
sockets->first_fd(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf),
MSG_DONTWAIT),
SyscallFailsWithErrno(EAGAIN));
}
// Check that not setting a default send interface prevents multicast packets
// from being sent. Group membership interface configured by address.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackAddrNoDefaultSendIf) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Bind the second FD to the v4 any address to ensure that we can receive any
// unicast packet.
sockaddr_in receiverAddr = {};
receiverAddr.sin_family = AF_INET;
receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
EXPECT_THAT(
bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr),
sizeof(receiverAddr)),
SyscallSucceeds());
socklen_t receiverAddrLen = sizeof(receiverAddr);
EXPECT_THAT(
getsockname(sockets->second_fd(),
reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen),
SyscallSucceeds());
EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr));
// Register to receive multicast packets.
ip_mreq group = {};
group.imr_multiaddr.s_addr = inet_addr("224.0.2.1");
group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
// Send a multicast packet.
sockaddr_in sendAddr = {};
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = receiverAddr.sin_port;
sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1");
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
EXPECT_THAT(RetryEINTR(sendto)(
sockets->first_fd(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)),
SyscallFailsWithErrno(ENETUNREACH));
}
// Check that not setting a default send interface prevents multicast packets
// from being sent. Group membership interface configured by NIC ID.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNicNoDefaultSendIf) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Bind the second FD to the v4 any address to ensure that we can receive any
// unicast packet.
sockaddr_in receiverAddr = {};
receiverAddr.sin_family = AF_INET;
receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
EXPECT_THAT(
bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr),
sizeof(receiverAddr)),
SyscallSucceeds());
socklen_t receiverAddrLen = sizeof(receiverAddr);
EXPECT_THAT(
getsockname(sockets->second_fd(),
reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen),
SyscallSucceeds());
EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr));
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr("224.0.2.1");
group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
// Send a multicast packet.
sockaddr_in sendAddr = {};
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = receiverAddr.sin_port;
sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1");
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
EXPECT_THAT(RetryEINTR(sendto)(
sockets->first_fd(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)),
SyscallFailsWithErrno(ENETUNREACH));
}
// Check that multicast works when the default send interface is configured by
// bind and the group membership is configured by address.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackAddr) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Bind the first FD to the loopback. This is an alternative to
// IP_MULTICAST_IF for setting the default send interface.
sockaddr_in senderAddr = {};
senderAddr.sin_family = AF_INET;
senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(
bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr),
sizeof(senderAddr)),
SyscallSucceeds());
// Bind the second FD to the v4 any address to ensure that we can receive the
// multicast packet.
sockaddr_in receiverAddr = {};
receiverAddr.sin_family = AF_INET;
receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
EXPECT_THAT(
bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr),
sizeof(receiverAddr)),
SyscallSucceeds());
socklen_t receiverAddrLen = sizeof(receiverAddr);
EXPECT_THAT(
getsockname(sockets->second_fd(),
reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen),
SyscallSucceeds());
EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr));
// Register to receive multicast packets.
ip_mreq group = {};
group.imr_multiaddr.s_addr = inet_addr("224.0.2.1");
group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
// Send a multicast packet.
sockaddr_in sendAddr = {};
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = receiverAddr.sin_port;
sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1");
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
EXPECT_THAT(RetryEINTR(sendto)(
sockets->first_fd(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
ASSERT_THAT(
RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), 0),
SyscallSucceedsWithValue(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
// Check that multicast works when the default send interface is confgured by
// bind and the group membership is configured by NIC ID.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNic) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Bind the first FD to the loopback. This is an alternative to
// IP_MULTICAST_IF for setting the default send interface.
sockaddr_in senderAddr = {};
senderAddr.sin_family = AF_INET;
senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(
bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr),
sizeof(senderAddr)),
SyscallSucceeds());
// Bind the second FD to the v4 any address to ensure that we can receive the
// multicast packet.
sockaddr_in receiverAddr = {};
receiverAddr.sin_family = AF_INET;
receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
EXPECT_THAT(
bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr),
sizeof(receiverAddr)),
SyscallSucceeds());
socklen_t receiverAddrLen = sizeof(receiverAddr);
EXPECT_THAT(
getsockname(sockets->second_fd(),
reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen),
SyscallSucceeds());
EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr));
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr("224.0.2.1");
group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
// Send a multicast packet.
sockaddr_in sendAddr = {};
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = receiverAddr.sin_port;
sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1");
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
EXPECT_THAT(RetryEINTR(sendto)(
sockets->first_fd(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
ASSERT_THAT(
RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), 0),
SyscallSucceedsWithValue(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
// Check that dropping a group membership that does not exist fails.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastInvalidDrop) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Unregister from a membership that we didn't have.
ip_mreq group = {};
group.imr_multiaddr.s_addr = inet_addr("224.0.2.1");
group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(setsockopt(sockets->first_fd(), 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.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastDropAddr) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Bind the first FD to the loopback. This is an alternative to
// IP_MULTICAST_IF for setting the default send interface.
sockaddr_in senderAddr = {};
senderAddr.sin_family = AF_INET;
senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(
bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr),
sizeof(senderAddr)),
SyscallSucceeds());
// Bind the second FD to the v4 any address to ensure that we can receive the
// multicast packet.
sockaddr_in receiverAddr = {};
receiverAddr.sin_family = AF_INET;
receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
EXPECT_THAT(
bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr),
sizeof(receiverAddr)),
SyscallSucceeds());
socklen_t receiverAddrLen = sizeof(receiverAddr);
EXPECT_THAT(
getsockname(sockets->second_fd(),
reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen),
SyscallSucceeds());
EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr));
// Register and unregister to receive multicast packets.
ip_mreq group = {};
group.imr_multiaddr.s_addr = inet_addr("224.0.2.1");
group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_DROP_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
// Send a multicast packet.
sockaddr_in sendAddr = {};
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = receiverAddr.sin_port;
sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1");
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
EXPECT_THAT(RetryEINTR(sendto)(
sockets->first_fd(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf),
MSG_DONTWAIT),
SyscallFailsWithErrno(EAGAIN));
}
// Check that dropping a group membership prevents multicast packets from being
// delivered. Default send address configured by bind and group membership
// interface configured by NIC ID.
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastDropNic) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Bind the first FD to the loopback. This is an alternative to
// IP_MULTICAST_IF for setting the default send interface.
sockaddr_in senderAddr = {};
senderAddr.sin_family = AF_INET;
senderAddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
EXPECT_THAT(
bind(sockets->first_fd(), reinterpret_cast<sockaddr*>(&senderAddr),
sizeof(senderAddr)),
SyscallSucceeds());
// Bind the second FD to the v4 any address to ensure that we can receive the
// multicast packet.
sockaddr_in receiverAddr = {};
receiverAddr.sin_family = AF_INET;
receiverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
EXPECT_THAT(
bind(sockets->second_fd(), reinterpret_cast<sockaddr*>(&receiverAddr),
sizeof(receiverAddr)),
SyscallSucceeds());
socklen_t receiverAddrLen = sizeof(receiverAddr);
EXPECT_THAT(
getsockname(sockets->second_fd(),
reinterpret_cast<sockaddr*>(&receiverAddr), &receiverAddrLen),
SyscallSucceeds());
EXPECT_EQ(receiverAddrLen, sizeof(receiverAddr));
// Register and unregister to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr("224.0.2.1");
group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
EXPECT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_DROP_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
// Send a multicast packet.
sockaddr_in sendAddr = {};
sendAddr.sin_family = AF_INET;
sendAddr.sin_port = receiverAddr.sin_port;
sendAddr.sin_addr.s_addr = inet_addr("224.0.2.1");
char send_buf[200];
RandomizeBuffer(send_buf, sizeof(send_buf));
EXPECT_THAT(RetryEINTR(sendto)(
sockets->first_fd(), send_buf, sizeof(send_buf), 0,
reinterpret_cast<sockaddr*>(&sendAddr), sizeof(sendAddr)),
SyscallSucceedsWithValue(sizeof(send_buf)));
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf),
MSG_DONTWAIT),
SyscallFailsWithErrno(EAGAIN));
}
} // namespace testing
} // namespace gvisor
@@ -0,0 +1,29 @@
// Copyright 2019 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_IPV4_UDP_UNBOUND_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_
#include "test/syscalls/linux/socket_test_util.h"
namespace gvisor {
namespace testing {
// Test fixture for tests that apply to pairs of IPv4 UDP sockets.
using IPv4UDPUnboundSocketPairTest = SocketPairTest;
} // namespace testing
} // namespace gvisor
#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_
@@ -0,0 +1,35 @@
// 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 <vector>
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_ipv4_udp_unbound.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVec<SocketPairKind>(
IPv4UDPUnboundSocketPair,
AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK}));
}
INSTANTIATE_TEST_CASE_P(IPv4UDPSockets, IPv4UDPUnboundSocketPairTest,
::testing::ValuesIn(GetSocketPairs()));
} // namespace testing
} // namespace gvisor
+32 -8
View File
@@ -389,23 +389,32 @@ Creator<SocketPair> TCPAcceptBindSocketPairCreator(int domain, int type,
}
template <typename T>
PosixErrorOr<std::unique_ptr<AddrFDSocketPair>>
CreateUDPBidirectionalBindSocketPair(int sock1, int sock2, int type,
bool dual_stack) {
PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> CreateUDPBoundSocketPair(
int sock1, int sock2, int type, bool dual_stack) {
ASSIGN_OR_RETURN_ERRNO(T addr1, BindIP<T>(sock1, dual_stack));
ASSIGN_OR_RETURN_ERRNO(T addr2, BindIP<T>(sock2, dual_stack));
return absl::make_unique<AddrFDSocketPair>(sock1, sock2, addr1, addr2);
}
template <typename T>
PosixErrorOr<std::unique_ptr<AddrFDSocketPair>>
CreateUDPBidirectionalBindSocketPair(int sock1, int sock2, int type,
bool dual_stack) {
ASSIGN_OR_RETURN_ERRNO(
auto socks, CreateUDPBoundSocketPair<T>(sock1, sock2, type, dual_stack));
// Connect sock1 to sock2.
RETURN_ERROR_IF_SYSCALL_FAIL(connect(
sock1, reinterpret_cast<struct sockaddr*>(&addr2), sizeof(addr2)));
RETURN_ERROR_IF_SYSCALL_FAIL(connect(socks->first_fd(), socks->second_addr(),
socks->second_addr_size()));
MaybeSave(); // Successful connection.
// Connect sock2 to sock1.
RETURN_ERROR_IF_SYSCALL_FAIL(connect(
sock2, reinterpret_cast<struct sockaddr*>(&addr1), sizeof(addr1)));
RETURN_ERROR_IF_SYSCALL_FAIL(connect(socks->second_fd(), socks->first_addr(),
socks->first_addr_size()));
MaybeSave(); // Successful connection.
return absl::make_unique<AddrFDSocketPair>(sock1, sock2, addr1, addr2);
return socks;
}
Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type,
@@ -429,6 +438,21 @@ Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type,
};
}
Creator<SocketPair> UDPUnboundSocketPairCreator(int domain, int type,
int protocol, bool dual_stack) {
return [=]() -> PosixErrorOr<std::unique_ptr<FDSocketPair>> {
int sock1;
RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol));
MaybeSave(); // Successful socket creation.
int sock2;
RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol));
MaybeSave(); // Successful socket creation.
return absl::make_unique<FDSocketPair>(sock1, sock2);
};
}
SocketPairKind Reversed(SocketPairKind const& base) {
auto const& creator = base.creator;
return SocketPairKind{
+5
View File
@@ -273,6 +273,11 @@ Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type,
int protocol,
bool dual_stack);
// UDPUnboundSocketPairCreator returns a Creator<SocketPair> that obtains file
// descriptors by creating UDP sockets.
Creator<SocketPair> UDPUnboundSocketPairCreator(int domain, int type,
int protocol, bool dual_stack);
// A SocketPairKind couples a human-readable description of a socket pair with
// a function that creates such a socket pair.
struct SocketPairKind {