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:
Ian Gudger
2019-03-09 11:40:51 -08:00
committed by Shentubot
parent 281092e842
commit 71d53382bf
2 changed files with 201 additions and 24 deletions
+29 -22
View File
@@ -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
}
+172 -2
View File
@@ -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());