mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
+18
-3
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user