mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement IP_MULTICAST_IF.
This allows setting a default send interface for IPv4 multicast. IPv6 support will come later. PiperOrigin-RevId: 234251379 Change-Id: I65922341cd8b8880f690fae3eeb7ddfa47c8c173
This commit is contained in:
@@ -27,7 +27,6 @@ package epsocket
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
@@ -191,6 +190,15 @@ func New(t *kernel.Task, family int, skType transport.SockType, queue *waiter.Qu
|
||||
var sockAddrInetSize = int(binary.Size(linux.SockAddrInet{}))
|
||||
var sockAddrInet6Size = int(binary.Size(linux.SockAddrInet6{}))
|
||||
|
||||
// bytesToIPAddress converts an IPv4 or IPv6 address from the user to the
|
||||
// netstack representation taking any addresses into account.
|
||||
func bytesToIPAddress(addr []byte) tcpip.Address {
|
||||
if bytes.Equal(addr, make([]byte, 4)) || bytes.Equal(addr, make([]byte, 16)) {
|
||||
return ""
|
||||
}
|
||||
return tcpip.Address(addr)
|
||||
}
|
||||
|
||||
// GetAddress reads an sockaddr struct from the given address and converts it
|
||||
// to the FullAddress format. It supports AF_UNIX, AF_INET and AF_INET6
|
||||
// addresses.
|
||||
@@ -231,12 +239,9 @@ func GetAddress(sfamily int, addr []byte) (tcpip.FullAddress, *syserr.Error) {
|
||||
binary.Unmarshal(addr[:sockAddrInetSize], usermem.ByteOrder, &a)
|
||||
|
||||
out := tcpip.FullAddress{
|
||||
Addr: tcpip.Address(a.Addr[:]),
|
||||
Addr: bytesToIPAddress(a.Addr[:]),
|
||||
Port: ntohs(a.Port),
|
||||
}
|
||||
if out.Addr == "\x00\x00\x00\x00" {
|
||||
out.Addr = ""
|
||||
}
|
||||
return out, nil
|
||||
|
||||
case linux.AF_INET6:
|
||||
@@ -247,15 +252,12 @@ func GetAddress(sfamily int, addr []byte) (tcpip.FullAddress, *syserr.Error) {
|
||||
binary.Unmarshal(addr[:sockAddrInet6Size], usermem.ByteOrder, &a)
|
||||
|
||||
out := tcpip.FullAddress{
|
||||
Addr: tcpip.Address(a.Addr[:]),
|
||||
Addr: bytesToIPAddress(a.Addr[:]),
|
||||
Port: ntohs(a.Port),
|
||||
}
|
||||
if isLinkLocal(out.Addr) {
|
||||
out.NIC = tcpip.NICID(a.Scope_id)
|
||||
}
|
||||
if out.Addr == tcpip.Address(strings.Repeat("\x00", 16)) {
|
||||
out.Addr = ""
|
||||
}
|
||||
return out, nil
|
||||
|
||||
default:
|
||||
@@ -864,6 +866,30 @@ func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfac
|
||||
|
||||
return int32(v), nil
|
||||
|
||||
case linux.IP_MULTICAST_IF:
|
||||
if outLen < inetMulticastRequestSize {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
var v tcpip.MulticastInterfaceOption
|
||||
if err := ep.GetSockOpt(&v); err != nil {
|
||||
return nil, syserr.TranslateNetstackError(err)
|
||||
}
|
||||
|
||||
a, _ := ConvertAddress(linux.AF_INET, tcpip.FullAddress{Addr: v.InterfaceAddr})
|
||||
|
||||
rv := linux.InetMulticastRequestWithNIC{
|
||||
linux.InetMulticastRequest{
|
||||
InterfaceAddr: a.(linux.SockAddrInet).Addr,
|
||||
},
|
||||
int32(v.NIC),
|
||||
}
|
||||
|
||||
if outLen >= inetMulticastRequestWithNICSize {
|
||||
return rv, nil
|
||||
}
|
||||
return rv.InetMulticastRequest, nil
|
||||
|
||||
default:
|
||||
emitUnimplementedEventIP(t, name)
|
||||
}
|
||||
@@ -1148,7 +1174,9 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
|
||||
}
|
||||
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.AddMembershipOption{
|
||||
NIC: tcpip.NICID(req.InterfaceIndex),
|
||||
NIC: tcpip.NICID(req.InterfaceIndex),
|
||||
// TODO: Change AddMembership to use the standard
|
||||
// any address representation.
|
||||
InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]),
|
||||
MulticastAddr: tcpip.Address(req.MulticastAddr[:]),
|
||||
}))
|
||||
@@ -1160,19 +1188,29 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
|
||||
}
|
||||
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.RemoveMembershipOption{
|
||||
NIC: tcpip.NICID(req.InterfaceIndex),
|
||||
NIC: tcpip.NICID(req.InterfaceIndex),
|
||||
// TODO: Change DropMembership to use the standard
|
||||
// any address representation.
|
||||
InterfaceAddr: tcpip.Address(req.InterfaceAddr[:]),
|
||||
MulticastAddr: tcpip.Address(req.MulticastAddr[:]),
|
||||
}))
|
||||
|
||||
case linux.IP_MULTICAST_IF:
|
||||
req, err := copyInMulticastRequest(optVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(tcpip.MulticastInterfaceOption{
|
||||
NIC: tcpip.NICID(req.InterfaceIndex),
|
||||
InterfaceAddr: bytesToIPAddress(req.InterfaceAddr[:]),
|
||||
}))
|
||||
|
||||
case linux.MCAST_JOIN_GROUP:
|
||||
// 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
|
||||
// still allow setting TTL, and multicast-enable/disable type options.
|
||||
fallthrough
|
||||
case linux.MCAST_JOIN_GROUP:
|
||||
// FIXME: Implement MCAST_JOIN_GROUP.
|
||||
t.Kernel().EmitUnimplementedEvent(t)
|
||||
return syserr.ErrInvalidArgument
|
||||
|
||||
|
||||
@@ -565,6 +565,17 @@ func (s *Stack) EnableNIC(id tcpip.NICID) *tcpip.Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckNIC checks if a NIC is usable.
|
||||
func (s *Stack) CheckNIC(id tcpip.NICID) bool {
|
||||
s.mu.RLock()
|
||||
nic, ok := s.nics[id]
|
||||
s.mu.RUnlock()
|
||||
if ok {
|
||||
return nic.linkEP.IsAttached()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NICSubnets returns a map of NICIDs to their associated subnets.
|
||||
func (s *Stack) NICSubnets() map[tcpip.NICID][]tcpip.Subnet {
|
||||
s.mu.RLock()
|
||||
|
||||
@@ -473,6 +473,13 @@ type KeepaliveCountOption int
|
||||
// TTL value for multicast messages. The default is 1.
|
||||
type MulticastTTLOption uint8
|
||||
|
||||
// MulticastInterfaceOption is used by SetSockOpt/GetSockOpt to specify a
|
||||
// default interface for multicast.
|
||||
type MulticastInterfaceOption struct {
|
||||
NIC NICID
|
||||
InterfaceAddr Address
|
||||
}
|
||||
|
||||
// MembershipOption is used by SetSockOpt/GetSockOpt as an argument to
|
||||
// AddMembershipOption and RemoveMembershipOption.
|
||||
type MembershipOption struct {
|
||||
|
||||
@@ -69,17 +69,19 @@ type endpoint struct {
|
||||
rcvClosed bool
|
||||
|
||||
// The following fields are protected by the mu mutex.
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
sndBufSize int
|
||||
id stack.TransportEndpointID
|
||||
state endpointState
|
||||
bindNICID tcpip.NICID
|
||||
regNICID tcpip.NICID
|
||||
route stack.Route `state:"manual"`
|
||||
dstPort uint16
|
||||
v6only bool
|
||||
multicastTTL uint8
|
||||
reusePort bool
|
||||
mu sync.RWMutex `state:"nosave"`
|
||||
sndBufSize int
|
||||
id stack.TransportEndpointID
|
||||
state endpointState
|
||||
bindNICID tcpip.NICID
|
||||
regNICID tcpip.NICID
|
||||
route stack.Route `state:"manual"`
|
||||
dstPort uint16
|
||||
v6only bool
|
||||
multicastTTL uint8
|
||||
multicastAddr tcpip.Address
|
||||
multicastNICID tcpip.NICID
|
||||
reusePort bool
|
||||
|
||||
// shutdownFlags represent the current shutdown state of the endpoint.
|
||||
shutdownFlags tcpip.ShutdownFlags
|
||||
@@ -251,6 +253,33 @@ func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpi
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// connectRoute establishes a route to the specified interface or the
|
||||
// configured multicast interface if no interface is specified and the
|
||||
// specified address is a multicast address.
|
||||
func (e *endpoint) connectRoute(nicid tcpip.NICID, addr tcpip.FullAddress) (stack.Route, tcpip.NICID, tcpip.NetworkProtocolNumber, *tcpip.Error) {
|
||||
netProto, err := e.checkV4Mapped(&addr, false)
|
||||
if err != nil {
|
||||
return stack.Route{}, 0, 0, err
|
||||
}
|
||||
|
||||
localAddr := e.id.LocalAddress
|
||||
if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
|
||||
if nicid == 0 {
|
||||
nicid = e.multicastNICID
|
||||
}
|
||||
if localAddr == "" {
|
||||
localAddr = e.multicastAddr
|
||||
}
|
||||
}
|
||||
|
||||
// Find a route to the desired destination.
|
||||
r, err := e.stack.FindRoute(nicid, localAddr, addr.Addr, netProto)
|
||||
if err != nil {
|
||||
return stack.Route{}, 0, 0, err
|
||||
}
|
||||
return r, nicid, netProto, nil
|
||||
}
|
||||
|
||||
// Write writes data to the endpoint's peer. This method does not block
|
||||
// if the data cannot be written.
|
||||
func (e *endpoint) Write(p tcpip.Payload, opts tcpip.WriteOptions) (uintptr, <-chan struct{}, *tcpip.Error) {
|
||||
@@ -318,15 +347,7 @@ func (e *endpoint) Write(p tcpip.Payload, opts tcpip.WriteOptions) (uintptr, <-c
|
||||
nicid = e.bindNICID
|
||||
}
|
||||
|
||||
toCopy := *to
|
||||
to = &toCopy
|
||||
netProto, err := e.checkV4Mapped(to, false)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Find the enpoint.
|
||||
r, err := e.stack.FindRoute(nicid, e.id.LocalAddress, to.Addr, netProto)
|
||||
r, _, _, err := e.connectRoute(nicid, *to)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
@@ -394,6 +415,42 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
|
||||
e.multicastTTL = uint8(v)
|
||||
e.mu.Unlock()
|
||||
|
||||
case tcpip.MulticastInterfaceOption:
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
fa := tcpip.FullAddress{Addr: v.InterfaceAddr}
|
||||
netProto, err := e.checkV4Mapped(&fa, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nic := v.NIC
|
||||
addr := fa.Addr
|
||||
|
||||
if nic == 0 && addr == "" {
|
||||
e.multicastAddr = ""
|
||||
e.multicastNICID = 0
|
||||
break
|
||||
}
|
||||
|
||||
if nic != 0 {
|
||||
if !e.stack.CheckNIC(nic) {
|
||||
return tcpip.ErrBadLocalAddress
|
||||
}
|
||||
} else {
|
||||
nic = e.stack.CheckLocalAddress(0, netProto, addr)
|
||||
if nic == 0 {
|
||||
return tcpip.ErrBadLocalAddress
|
||||
}
|
||||
}
|
||||
|
||||
if e.bindNICID != 0 && e.bindNICID != nic {
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
e.multicastNICID = nic
|
||||
e.multicastAddr = addr
|
||||
|
||||
case tcpip.AddMembershipOption:
|
||||
nicID := v.NIC
|
||||
if v.InterfaceAddr != header.IPv4Any {
|
||||
@@ -445,7 +502,6 @@ func (e *endpoint) SetSockOpt(opt interface{}) *tcpip.Error {
|
||||
e.mu.Lock()
|
||||
e.reusePort = v != 0
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -501,6 +557,15 @@ func (e *endpoint) GetSockOpt(opt interface{}) *tcpip.Error {
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.MulticastInterfaceOption:
|
||||
e.mu.Lock()
|
||||
*o = tcpip.MulticastInterfaceOption{
|
||||
e.multicastNICID,
|
||||
e.multicastAddr,
|
||||
}
|
||||
e.mu.Unlock()
|
||||
return nil
|
||||
|
||||
case *tcpip.ReusePortOption:
|
||||
e.mu.RLock()
|
||||
v := e.reusePort
|
||||
@@ -610,13 +675,7 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
|
||||
return tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
netProto, err := e.checkV4Mapped(&addr, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Find a route to the desired destination.
|
||||
r, err := e.stack.FindRoute(nicid, e.id.LocalAddress, addr.Addr, netProto)
|
||||
r, nicid, netProto, err := e.connectRoute(nicid, addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -278,6 +278,238 @@ TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackNic) {
|
||||
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
|
||||
}
|
||||
|
||||
// Check that multicast works when the default send interface is confgured by
|
||||
// IP_MULTICAST_IF, the send address is specified in sendto, and the group
|
||||
// membership is configured by address.
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackIfAddr) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
// Set the default send interface.
|
||||
ip_mreq iface = {};
|
||||
iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&iface, sizeof(iface)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Bind the second FD to the v4 any address to ensure that we can receive the
|
||||
// multicast packet.
|
||||
auto receiver_addr = V4Any();
|
||||
EXPECT_THAT(bind(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
EXPECT_THAT(getsockname(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
|
||||
// Register to receive multicast packets.
|
||||
ip_mreq group = {};
|
||||
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
|
||||
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.
|
||||
auto send_addr = V4Multicast();
|
||||
reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port =
|
||||
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
|
||||
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*>(&send_addr.addr),
|
||||
send_addr.addr_len),
|
||||
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
|
||||
// IP_MULTICAST_IF, the send address is specified in sendto, and the group
|
||||
// membership is configured by NIC ID.
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackIfNic) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
// Set the default send interface.
|
||||
ip_mreqn iface = {};
|
||||
iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&iface, sizeof(iface)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Bind the second FD to the v4 any address to ensure that we can receive the
|
||||
// multicast packet.
|
||||
auto receiver_addr = V4Any();
|
||||
EXPECT_THAT(bind(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
EXPECT_THAT(getsockname(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
|
||||
// Register to receive multicast packets.
|
||||
ip_mreqn group = {};
|
||||
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
|
||||
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.
|
||||
auto send_addr = V4Multicast();
|
||||
reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port =
|
||||
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
|
||||
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*>(&send_addr.addr),
|
||||
send_addr.addr_len),
|
||||
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
|
||||
// IP_MULTICAST_IF, the send address is specified in connect, and the group
|
||||
// membership is configured by address.
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackIfAddrConnect) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
// Set the default send interface.
|
||||
ip_mreq iface = {};
|
||||
iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&iface, sizeof(iface)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Bind the second FD to the v4 any address to ensure that we can receive the
|
||||
// multicast packet.
|
||||
auto receiver_addr = V4Any();
|
||||
EXPECT_THAT(bind(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
EXPECT_THAT(getsockname(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
|
||||
// Register to receive multicast packets.
|
||||
ip_mreq group = {};
|
||||
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
|
||||
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.
|
||||
auto connect_addr = V4Multicast();
|
||||
reinterpret_cast<sockaddr_in*>(&connect_addr.addr)->sin_port =
|
||||
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
|
||||
EXPECT_THAT(
|
||||
RetryEINTR(connect)(sockets->first_fd(),
|
||||
reinterpret_cast<sockaddr*>(&connect_addr.addr),
|
||||
connect_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
char send_buf[200];
|
||||
RandomizeBuffer(send_buf, sizeof(send_buf));
|
||||
EXPECT_THAT(
|
||||
RetryEINTR(send)(sockets->first_fd(), send_buf, sizeof(send_buf), 0),
|
||||
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
|
||||
// IP_MULTICAST_IF, the send address is specified in connect, and the group
|
||||
// membership is configured by NIC ID.
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastLoopbackIfNicConnect) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
// Set the default send interface.
|
||||
ip_mreqn iface = {};
|
||||
iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&iface, sizeof(iface)),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Bind the second FD to the v4 any address to ensure that we can receive the
|
||||
// multicast packet.
|
||||
auto receiver_addr = V4Any();
|
||||
EXPECT_THAT(bind(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
EXPECT_THAT(getsockname(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
|
||||
|
||||
// Register to receive multicast packets.
|
||||
ip_mreqn group = {};
|
||||
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
|
||||
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.
|
||||
auto connect_addr = V4Multicast();
|
||||
reinterpret_cast<sockaddr_in*>(&connect_addr.addr)->sin_port =
|
||||
reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port;
|
||||
EXPECT_THAT(
|
||||
RetryEINTR(connect)(sockets->first_fd(),
|
||||
reinterpret_cast<sockaddr*>(&connect_addr.addr),
|
||||
connect_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
|
||||
char send_buf[200];
|
||||
RandomizeBuffer(send_buf, sizeof(send_buf));
|
||||
EXPECT_THAT(
|
||||
RetryEINTR(send)(sockets->first_fd(), send_buf, sizeof(send_buf), 0),
|
||||
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());
|
||||
@@ -407,5 +639,34 @@ TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastDropNic) {
|
||||
SyscallFailsWithErrno(EAGAIN));
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfZero) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreqn iface = {};
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&iface, sizeof(iface)),
|
||||
SyscallSucceeds());
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfInvalidNic) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreqn iface = {};
|
||||
iface.imr_ifindex = -1;
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&iface, sizeof(iface)),
|
||||
SyscallFailsWithErrno(EADDRNOTAVAIL));
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfInvalidAddr) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreq iface = {};
|
||||
iface.imr_interface.s_addr = inet_addr("255.255.255");
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF,
|
||||
&iface, sizeof(iface)),
|
||||
SyscallFailsWithErrno(EADDRNOTAVAIL));
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
Reference in New Issue
Block a user