mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Fix getsockopt(IP_MULTICAST_IF).
getsockopt(IP_MULTICAST_IF) only supports struct in_addr. Also adds support for setsockopt(IP_MULTICAST_IF) with struct in_addr. PiperOrigin-RevId: 237620230 Change-Id: I75e7b5b3e08972164eb1906f43ddd67aedffc27c
This commit is contained in:
@@ -888,7 +888,7 @@ func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfac
|
||||
return int32(v), nil
|
||||
|
||||
case linux.IP_MULTICAST_IF:
|
||||
if outLen < inetMulticastRequestSize {
|
||||
if outLen < len(linux.InetAddr{}) {
|
||||
return nil, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
@@ -899,17 +899,7 @@ func getSockOptIP(t *kernel.Task, ep commonEndpoint, name, outLen int) (interfac
|
||||
|
||||
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
|
||||
return a.(linux.SockAddrInet).Addr, nil
|
||||
|
||||
case linux.IP_MULTICAST_LOOP:
|
||||
if outLen < sizeOfInt32 {
|
||||
@@ -1179,17 +1169,34 @@ var (
|
||||
inetMulticastRequestWithNICSize = int(binary.Size(linux.InetMulticastRequestWithNIC{}))
|
||||
)
|
||||
|
||||
func copyInMulticastRequest(optVal []byte) (linux.InetMulticastRequestWithNIC, *syserr.Error) {
|
||||
if len(optVal) < inetMulticastRequestSize {
|
||||
// copyInMulticastRequest copies in a variable-size multicast request. The
|
||||
// kernel determines which structure was passed by its length. IP_MULTICAST_IF
|
||||
// supports ip_mreqn, ip_mreq and in_addr, while IP_ADD_MEMBERSHIP and
|
||||
// IP_DROP_MEMBERSHIP only support ip_mreqn and ip_mreq. To handle this,
|
||||
// allowAddr controls whether in_addr is accepted or rejected.
|
||||
func copyInMulticastRequest(optVal []byte, allowAddr bool) (linux.InetMulticastRequestWithNIC, *syserr.Error) {
|
||||
if len(optVal) < len(linux.InetAddr{}) {
|
||||
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)
|
||||
if len(optVal) < inetMulticastRequestSize {
|
||||
if !allowAddr {
|
||||
return linux.InetMulticastRequestWithNIC{}, syserr.ErrInvalidArgument
|
||||
}
|
||||
|
||||
var req linux.InetMulticastRequestWithNIC
|
||||
copy(req.InterfaceAddr[:], optVal)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
if len(optVal) >= inetMulticastRequestWithNICSize {
|
||||
var req linux.InetMulticastRequestWithNIC
|
||||
binary.Unmarshal(optVal[:inetMulticastRequestWithNICSize], usermem.ByteOrder, &req)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
var req linux.InetMulticastRequestWithNIC
|
||||
binary.Unmarshal(optVal[:inetMulticastRequestSize], usermem.ByteOrder, &req.InetMulticastRequest)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
@@ -1227,7 +1234,7 @@ 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:
|
||||
req, err := copyInMulticastRequest(optVal)
|
||||
req, err := copyInMulticastRequest(optVal, false /* allowAddr */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1241,7 +1248,7 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
|
||||
}))
|
||||
|
||||
case linux.IP_DROP_MEMBERSHIP:
|
||||
req, err := copyInMulticastRequest(optVal)
|
||||
req, err := copyInMulticastRequest(optVal, false /* allowAddr */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1255,7 +1262,7 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
|
||||
}))
|
||||
|
||||
case linux.IP_MULTICAST_IF:
|
||||
req, err := copyInMulticastRequest(optVal)
|
||||
req, err := copyInMulticastRequest(optVal, true /* allowAddr */)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -893,7 +893,7 @@ TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastDropAddr) {
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
EXPECT_THAT(getsockname(sockets->second_fd(),
|
||||
ASSERT_THAT(getsockname(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
@@ -951,7 +951,7 @@ TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastDropNic) {
|
||||
receiver_addr.addr_len),
|
||||
SyscallSucceeds());
|
||||
socklen_t receiver_addr_len = receiver_addr.addr_len;
|
||||
EXPECT_THAT(getsockname(sockets->second_fd(),
|
||||
ASSERT_THAT(getsockname(sockets->second_fd(),
|
||||
reinterpret_cast<sockaddr*>(&receiver_addr.addr),
|
||||
&receiver_addr_len),
|
||||
SyscallSucceeds());
|
||||
@@ -1016,6 +1016,176 @@ TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfInvalidAddr) {
|
||||
SyscallFailsWithErrno(EADDRNOTAVAIL));
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfSetShort) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
// Create a valid full-sized request.
|
||||
ip_mreqn iface = {};
|
||||
iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
|
||||
|
||||
// Send an optlen of 1 to check that optlen is enforced.
|
||||
EXPECT_THAT(
|
||||
setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &iface, 1),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfDefault) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
in_addr get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(size, sizeof(get));
|
||||
EXPECT_EQ(get.s_addr, 0);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfDefaultReqn) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreqn get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
|
||||
// getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
|
||||
// first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
|
||||
// Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
|
||||
EXPECT_EQ(size, sizeof(in_addr));
|
||||
|
||||
// getsockopt(IP_MULTICAST_IF) will only return the interface address which
|
||||
// hasn't been set.
|
||||
EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
|
||||
EXPECT_EQ(get.imr_address.s_addr, 0);
|
||||
EXPECT_EQ(get.imr_ifindex, 0);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfSetAddrGetReqn) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
in_addr set = {};
|
||||
set.s_addr = htonl(INADDR_LOOPBACK);
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &set,
|
||||
sizeof(set)),
|
||||
SyscallSucceeds());
|
||||
|
||||
ip_mreqn get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
|
||||
// getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
|
||||
// first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
|
||||
// Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
|
||||
EXPECT_EQ(size, sizeof(in_addr));
|
||||
EXPECT_EQ(get.imr_multiaddr.s_addr, set.s_addr);
|
||||
EXPECT_EQ(get.imr_address.s_addr, 0);
|
||||
EXPECT_EQ(get.imr_ifindex, 0);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfSetReqAddrGetReqn) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreq set = {};
|
||||
set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &set,
|
||||
sizeof(set)),
|
||||
SyscallSucceeds());
|
||||
|
||||
ip_mreqn get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
|
||||
// getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
|
||||
// first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
|
||||
// Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
|
||||
EXPECT_EQ(size, sizeof(in_addr));
|
||||
EXPECT_EQ(get.imr_multiaddr.s_addr, set.imr_interface.s_addr);
|
||||
EXPECT_EQ(get.imr_address.s_addr, 0);
|
||||
EXPECT_EQ(get.imr_ifindex, 0);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfSetNicGetReqn) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreqn set = {};
|
||||
set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &set,
|
||||
sizeof(set)),
|
||||
SyscallSucceeds());
|
||||
|
||||
ip_mreqn get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(size, sizeof(in_addr));
|
||||
EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
|
||||
EXPECT_EQ(get.imr_address.s_addr, 0);
|
||||
EXPECT_EQ(get.imr_ifindex, 0);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfSetAddr) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
in_addr set = {};
|
||||
set.s_addr = htonl(INADDR_LOOPBACK);
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &set,
|
||||
sizeof(set)),
|
||||
SyscallSucceeds());
|
||||
|
||||
in_addr get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
|
||||
EXPECT_EQ(size, sizeof(get));
|
||||
EXPECT_EQ(get.s_addr, set.s_addr);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfSetReqAddr) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreq set = {};
|
||||
set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &set,
|
||||
sizeof(set)),
|
||||
SyscallSucceeds());
|
||||
|
||||
in_addr get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
|
||||
EXPECT_EQ(size, sizeof(get));
|
||||
EXPECT_EQ(get.s_addr, set.imr_interface.s_addr);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, IpMulticastIfSetNic) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ip_mreqn set = {};
|
||||
set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &set,
|
||||
sizeof(set)),
|
||||
SyscallSucceeds());
|
||||
|
||||
in_addr get = {};
|
||||
socklen_t size = sizeof(get);
|
||||
ASSERT_THAT(
|
||||
getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
|
||||
SyscallSucceeds());
|
||||
EXPECT_EQ(size, sizeof(get));
|
||||
EXPECT_EQ(get.s_addr, 0);
|
||||
}
|
||||
|
||||
TEST_P(IPv4UDPUnboundSocketPairTest, TestJoinGroupNoIf) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user