mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Make IP_MULTICAST_LOOP and IP_MULTICAST_TTL allow setting int or char.
This is the correct Linux behavior, and at least PHP depends on it. PiperOrigin-RevId: 237565639 Change-Id: I931af09c8ed99a842cf70d22bfe0b65e330c4137
This commit is contained in:
@@ -1193,24 +1193,30 @@ func copyInMulticastRequest(optVal []byte) (linux.InetMulticastRequestWithNIC, *
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// reduceToByte ORs all of the bytes in the input.
|
||||
func reduceToByte(buf []byte) byte {
|
||||
var out byte
|
||||
for _, b := range buf {
|
||||
out |= b
|
||||
// parseIntOrChar copies either a 32-bit int or an 8-bit uint out of buf.
|
||||
//
|
||||
// net/ipv4/ip_sockglue.c:do_ip_setsockopt does this for its socket options.
|
||||
func parseIntOrChar(buf []byte) (int32, *syserr.Error) {
|
||||
if len(buf) == 0 {
|
||||
return 0, syserr.ErrInvalidArgument
|
||||
}
|
||||
return out
|
||||
|
||||
if len(buf) >= sizeOfInt32 {
|
||||
return int32(usermem.ByteOrder.Uint32(buf)), nil
|
||||
}
|
||||
|
||||
return int32(buf[0]), nil
|
||||
}
|
||||
|
||||
// setSockOptIP implements SetSockOpt when level is SOL_IP.
|
||||
func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *syserr.Error {
|
||||
switch name {
|
||||
case linux.IP_MULTICAST_TTL:
|
||||
if len(optVal) < sizeOfInt32 {
|
||||
return syserr.ErrInvalidArgument
|
||||
v, err := parseIntOrChar(optVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v := int32(usermem.ByteOrder.Uint32(optVal))
|
||||
if v == -1 {
|
||||
// Linux translates -1 to 1.
|
||||
v = 1
|
||||
@@ -1260,15 +1266,13 @@ func setSockOptIP(t *kernel.Task, ep commonEndpoint, name int, optVal []byte) *s
|
||||
}))
|
||||
|
||||
case linux.IP_MULTICAST_LOOP:
|
||||
if len(optVal) < 1 {
|
||||
return syserr.ErrInvalidArgument
|
||||
}
|
||||
if len(optVal) > sizeOfInt32 {
|
||||
optVal = optVal[:sizeOfInt32]
|
||||
v, err := parseIntOrChar(optVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return syserr.TranslateNetstackError(ep.SetSockOpt(
|
||||
tcpip.MulticastLoopOption(reduceToByte(optVal) != 0),
|
||||
tcpip.MulticastLoopOption(v != 0),
|
||||
))
|
||||
|
||||
case linux.MCAST_JOIN_GROUP:
|
||||
|
||||
@@ -117,6 +117,23 @@ TEST_P(UDPSocketPairTest, SetUDPMulticastTTLAboveMax) {
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
}
|
||||
|
||||
TEST_P(UDPSocketPairTest, SetUDPMulticastTTLChar) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
constexpr char kArbitrary = 6;
|
||||
EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
&kArbitrary, sizeof(kArbitrary)),
|
||||
SyscallSucceeds());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL,
|
||||
&get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kArbitrary);
|
||||
}
|
||||
|
||||
TEST_P(UDPSocketPairTest, SetEmptyIPAddMembership) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
@@ -126,5 +143,72 @@ TEST_P(UDPSocketPairTest, SetEmptyIPAddMembership) {
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
}
|
||||
|
||||
TEST_P(UDPSocketPairTest, MulticastLoopDefault) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOn);
|
||||
}
|
||||
|
||||
TEST_P(UDPSocketPairTest, SetMulticastLoop) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&kSockOptOff, sizeof(kSockOptOff)),
|
||||
SyscallSucceeds());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&kSockOptOn, sizeof(kSockOptOn)),
|
||||
SyscallSucceeds());
|
||||
|
||||
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOn);
|
||||
}
|
||||
|
||||
TEST_P(UDPSocketPairTest, SetMulticastLoopChar) {
|
||||
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
|
||||
|
||||
constexpr char kSockOptOnChar = kSockOptOn;
|
||||
constexpr char kSockOptOffChar = kSockOptOff;
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&kSockOptOffChar, sizeof(kSockOptOffChar)),
|
||||
SyscallSucceeds());
|
||||
|
||||
int get = -1;
|
||||
socklen_t get_len = sizeof(get);
|
||||
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOff);
|
||||
|
||||
ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&kSockOptOnChar, sizeof(kSockOptOnChar)),
|
||||
SyscallSucceeds());
|
||||
|
||||
EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP,
|
||||
&get, &get_len),
|
||||
SyscallSucceedsWithValue(0));
|
||||
EXPECT_EQ(get_len, sizeof(get));
|
||||
EXPECT_EQ(get, kSockOptOn);
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
|
||||
Reference in New Issue
Block a user