Skip to content

Commit 031e72d

Browse files
shailend-ggvisor-bot
authored andcommitted
Support IPv6_MULTICAST_IF
Of the 11 java runtime tests that were anticipating this socket option, 8 now pass. Of the 3 that still fail, 1 now needs IPV6_MULTICAST_HOPS. The other 2 fail due to an unknown reason: they fail with runc too. PiperOrigin-RevId: 813839126
1 parent 6f3d0ec commit 031e72d

File tree

5 files changed

+138
-14
lines changed

5 files changed

+138
-14
lines changed

pkg/sentry/socket/netstack/netstack.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1627,6 +1627,14 @@ func getSockOptIPv6(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int
16271627
return nil, err
16281628
}
16291629
return &ret, nil
1630+
1631+
case linux.IPV6_MULTICAST_IF:
1632+
v, err := ep.GetSockOptInt(tcpip.IPv6MulticastInterfaceOption)
1633+
if err != nil {
1634+
return nil, syserr.TranslateNetstackError(err)
1635+
}
1636+
vP := primitive.Int32(v)
1637+
return &vP, nil
16301638
}
16311639
return nil, syserr.ErrProtocolNotAvailable
16321640
}
@@ -2479,6 +2487,14 @@ func setSockOptIPv6(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int
24792487
MulticastAddr: tcpip.AddrFrom16(req.MulticastAddr),
24802488
}))
24812489

2490+
case linux.IPV6_MULTICAST_IF:
2491+
if len(optVal) < sizeOfInt32 {
2492+
return syserr.ErrInvalidArgument
2493+
}
2494+
2495+
v := int32(hostarch.ByteOrder.Uint32(optVal))
2496+
return syserr.TranslateNetstackError(ep.SetSockOptInt(tcpip.IPv6MulticastInterfaceOption, int(v)))
2497+
24822498
case linux.IPV6_IPSEC_POLICY,
24832499
linux.IPV6_JOIN_ANYCAST,
24842500
linux.IPV6_LEAVE_ANYCAST,
@@ -2595,7 +2611,6 @@ func setSockOptIPv6(t *kernel.Task, s socket.Socket, ep commonEndpoint, name int
25952611
linux.IPV6_MTU_DISCOVER,
25962612
linux.IPV6_FLOWINFO_SEND,
25972613
linux.IPV6_ADDR_PREFERENCES,
2598-
linux.IPV6_MULTICAST_IF,
25992614
linux.IPV6_UNICAST_IF,
26002615
linux.IPV6_ADDRFORM,
26012616
linux.IPV6_2292PKTINFO,

pkg/tcpip/tcpip.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,6 +1009,10 @@ const (
10091009
// PacketMMapReserveOption is used to set the packet mmap reserved space
10101010
// between the aligned header and the payload.
10111011
PacketMMapReserveOption
1012+
1013+
// IPv6MulticastInterfaceOption is used to set/get the NIC used for
1014+
// IPv6 multicast Tx.
1015+
IPv6MulticastInterfaceOption
10121016
)
10131017

10141018
const (

pkg/tcpip/transport/internal/network/endpoint.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,11 @@ type Endpoint struct {
6666
// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
6767
// +checklocks:mu
6868
multicastAddr tcpip.Address
69-
// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
7069
// +checklocks:mu
7170
multicastNICID tcpip.NICID
7271
// +checklocks:mu
72+
ipv6MulticastNICID tcpip.NICID
73+
// +checklocks:mu
7374
ipv4TOS uint8
7475
// +checklocks:mu
7576
ipv6TClass uint8
@@ -617,14 +618,17 @@ func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, localAddr tcpip.Addres
617618
localAddr = tcpip.Address{}
618619
}
619620

620-
if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
621+
if header.IsV4MulticastAddress(addr.Addr) {
621622
if nicID == 0 {
622623
nicID = e.multicastNICID
623624
}
624625
if localAddr == (tcpip.Address{}) && nicID == 0 {
625626
localAddr = e.multicastAddr
626627
}
627628
}
629+
if header.IsV6MulticastAddress(addr.Addr) && nicID == 0 {
630+
nicID = e.ipv6MulticastNICID
631+
}
628632
}
629633

630634
// Find a route to the desired destination.
@@ -869,6 +873,18 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
869873
e.mu.Lock()
870874
e.ipv6TClass = uint8(v)
871875
e.mu.Unlock()
876+
877+
case tcpip.IPv6MulticastInterfaceOption:
878+
if v != 0 && !e.stack.CheckNIC(tcpip.NICID(v)) {
879+
return &tcpip.ErrUnknownNICID{}
880+
}
881+
e.mu.Lock()
882+
defer e.mu.Unlock()
883+
nic := tcpip.NICID(v)
884+
if info := e.Info(); info.BindNICID != 0 && info.BindNICID != nic {
885+
return &tcpip.ErrInvalidEndpointState{}
886+
}
887+
e.ipv6MulticastNICID = nic
872888
}
873889

874890
return nil
@@ -911,6 +927,12 @@ func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
911927
e.mu.RUnlock()
912928
return v, nil
913929

930+
case tcpip.IPv6MulticastInterfaceOption:
931+
e.mu.RLock()
932+
v := int(e.ipv6MulticastNICID)
933+
e.mu.RUnlock()
934+
return v, nil
935+
914936
default:
915937
return -1, &tcpip.ErrUnknownProtocolOption{}
916938
}

test/runtimes/exclude/java21.csv

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,16 @@ java/lang/management/ThreadMXBean/ThreadMXBeanStateTest.java,,Broken test
2727
java/lang/reflect/exeCallerAccessTest/CallerAccessTest.java,,Broken test
2828
java/net/DatagramSocket/AddressNotSet.java,,
2929
java/net/DatagramSocket/SetGetReceiveBufferSize.java,b/180507650,
30-
java/net/MulticastSocket/B6425815.java,b/126900872,Need support for IPV6_MULTICAST_IF
31-
java/net/MulticastSocket/B6427403.java,b/126900872,Need support for IPV6_MULTICAST_IF
32-
java/net/MulticastSocket/IPMulticastIF.java,b/126900872,Need support for IPV6_MULTICAST_IF
30+
java/net/MulticastSocket/B6425815.java,b/446951345,Need support for IPV6_MULTICAST_HOPS
3331
java/net/MulticastSocket/MulticastTTL.java,,
34-
java/net/MulticastSocket/NetworkInterfaceEmptyGetInetAddressesTest.java,b/126900872,Need support for IPV6_MULTICAST_IF
35-
java/net/MulticastSocket/NoLoopbackPackets.java,b/126900872,Need support for IPV6_MULTICAST_IF
36-
java/net/MulticastSocket/NoSetNetworkInterface.java,b/126900872,Need support for IPV6_MULTICAST_IF
32+
java/net/MulticastSocket/NoLoopbackPackets.java,b/446928871,Test fails with runc too
3733
java/net/MulticastSocket/Promiscuous.java,,
3834
java/net/MulticastSocket/SendPortZero.java,,
3935
java/net/MulticastSocket/SetLoopbackMode.java,,
4036
java/net/MulticastSocket/SetLoopbackModeIPv4.java,,
4137
java/net/MulticastSocket/SetLoopbackOption.java,,
4238
java/net/MulticastSocket/SetTTLAndGetTTL.java,,
43-
java/net/MulticastSocket/Test.java,b/126900872,Need support for IPV6_MULTICAST_IF
39+
java/net/MulticastSocket/Test.java,b/446928871,Test fails with runc too
4440
java/net/MulticastSocket/TestDefaults.java,,
4541
java/net/MulticastSocket/TimeToLive.java,,
4642
java/net/NetworkInterface/NetworkInterfaceStreamTest.java,,
@@ -82,10 +78,6 @@ jdk/jfr/event/runtime/TestResidentSetSizeEvent.java,,
8278
jdk/jfr/jvm/TestWaste.java,,Broken test
8379
jdk/net/ExtendedSocketOption/DontFragmentTest.java,,
8480
jni/nullCaller/NullCallerTest.java,,Broken test
85-
sun/management/jdp/JdpDefaultsTest.java,b/126900872,Need support for IPV6_MULTICAST_IF
86-
sun/management/jdp/JdpJmxRemoteDynamicPortTest.java,b/126900872,Need support for IPV6_MULTICAST_IF
87-
sun/management/jdp/JdpOffTest.java,b/126900872,Need support for IPV6_MULTICAST_IF
88-
sun/management/jdp/JdpSpecificAddressTest.java,b/126900872,Need support for IPV6_MULTICAST_IF
8981
sun/management/jmxremote/bootstrap/CustomLauncherTest.java,,Broken test
9082
sun/management/jmxremote/bootstrap/JvmstatCountersTest.java,,Broken test
9183
sun/management/jmxremote/bootstrap/LocalManagementTest.java,,Broken test

test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,96 @@ TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, AddV4MembershipToV6Socket) {
211211
SyscallFailsWithErrno(ENOPROTOOPT));
212212
}
213213

214+
TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestIPv6MulticastIf) {
215+
auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
216+
217+
// Bind sock to the ethernet interface.
218+
struct sockaddr_in6 bind_addr = eth_if_addr();
219+
ASSERT_THAT(bind(sock->get(), AsSockAddr(&bind_addr), sizeof(bind_addr)),
220+
SyscallSucceeds());
221+
222+
int mcast_idx;
223+
socklen_t slen = sizeof(mcast_idx);
224+
ASSERT_THAT(getsockopt(sock->get(), IPPROTO_IPV6, IPV6_MULTICAST_IF,
225+
&mcast_idx, &slen),
226+
SyscallSucceeds());
227+
EXPECT_EQ(mcast_idx, 0); // Default is 0.
228+
229+
// Setting its multicast interface to the loopback interface should fail.
230+
mcast_idx = lo_if_idx();
231+
EXPECT_THAT(setsockopt(sock->get(), IPPROTO_IPV6, IPV6_MULTICAST_IF,
232+
&mcast_idx, slen),
233+
SyscallFailsWithErrno(EINVAL));
234+
235+
// But setting it to the ethernet interface should succeed.
236+
mcast_idx = eth_if_idx();
237+
EXPECT_THAT(setsockopt(sock->get(), IPPROTO_IPV6, IPV6_MULTICAST_IF,
238+
&mcast_idx, slen),
239+
SyscallSucceeds());
240+
ASSERT_THAT(getsockopt(sock->get(), IPPROTO_IPV6, IPV6_MULTICAST_IF,
241+
&mcast_idx, &slen),
242+
SyscallSucceeds());
243+
EXPECT_EQ(mcast_idx, eth_if_idx());
244+
}
245+
246+
TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest,
247+
TestIPv6MulticastIfSendAndRecv) {
248+
auto recv = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
249+
auto send = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
250+
251+
// Bind recv to ::.
252+
TestAddress recv_addr = V6Any();
253+
ASSERT_THAT(
254+
bind(recv->get(), AsSockAddr(&recv_addr.addr), recv_addr.addr_len),
255+
SyscallSucceeds());
256+
socklen_t recv_addr_len = recv_addr.addr_len;
257+
// Retrieve its assigned port.
258+
ASSERT_THAT(
259+
getsockname(recv->get(), AsSockAddr(&recv_addr.addr), &recv_addr_len),
260+
SyscallSucceeds());
261+
262+
// And have it join a multicast group on the loopback interface.
263+
TestAddress multicast_addr = V6Multicast();
264+
ipv6_mreq group_req = {
265+
.ipv6mr_multiaddr =
266+
reinterpret_cast<sockaddr_in6*>(&multicast_addr.addr)->sin6_addr,
267+
.ipv6mr_interface =
268+
static_cast<decltype(ipv6_mreq::ipv6mr_interface)>(lo_if_idx()),
269+
};
270+
ASSERT_THAT(setsockopt(recv->get(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
271+
&group_req, sizeof(group_req)),
272+
SyscallSucceeds());
273+
274+
// Set send's multicast interface to the loopback interface. It is still
275+
// unbound.
276+
int mcast_idx = lo_if_idx();
277+
ASSERT_THAT(setsockopt(send->get(), IPPROTO_IPV6, IPV6_MULTICAST_IF,
278+
&mcast_idx, sizeof(mcast_idx)),
279+
SyscallSucceeds());
280+
281+
// Send a multicast packet.
282+
auto send_addr = multicast_addr;
283+
reinterpret_cast<sockaddr_in6*>(&send_addr.addr)->sin6_port =
284+
reinterpret_cast<sockaddr_in6*>(&recv_addr.addr)->sin6_port;
285+
char send_buf[200];
286+
RandomizeBuffer(send_buf, sizeof(send_buf));
287+
ASSERT_THAT(
288+
RetryEINTR(sendto)(send->get(), send_buf, sizeof(send_buf), 0,
289+
AsSockAddr(&send_addr.addr), send_addr.addr_len),
290+
SyscallSucceedsWithValue(sizeof(send_buf)));
291+
292+
// Check that we received the multicast packet.
293+
char recv_buf[sizeof(send_buf)] = {};
294+
ASSERT_THAT(
295+
RecvTimeout(recv->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
296+
IsPosixErrorOkAndHolds(sizeof(recv_buf)));
297+
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
298+
299+
// Drop recv from the multicast group.
300+
ASSERT_THAT(setsockopt(recv->get(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP,
301+
&group_req, sizeof(group_req)),
302+
SyscallSucceeds());
303+
}
304+
214305
} // namespace testing
215306
} // namespace gvisor

0 commit comments

Comments
 (0)