Skip to content

Commit e6559a4

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: 803266836
1 parent 41a5b6c commit e6559a4

File tree

11 files changed

+340
-87
lines changed

11 files changed

+340
-87
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: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@ import (
4949

5050
// Using the header package here would cause an import cycle.
5151
const (
52-
ipv4AddressSize = 4
53-
ipv4ProtocolNumber = 0x0800
54-
ipv6AddressSize = 16
55-
ipv6ProtocolNumber = 0x86dd
52+
ipv4AddressSize = 4
53+
ipv6AddressSize = 16
5654
)
5755

5856
const (
@@ -1011,6 +1009,10 @@ const (
10111009
// PacketMMapReserveOption is used to set the packet mmap reserved space
10121010
// between the aligned header and the payload.
10131011
PacketMMapReserveOption
1012+
1013+
// IPv6MulticastInterfaceOption is used to set/get the NIC used for
1014+
// IPv6 multicast Tx.
1015+
IPv6MulticastInterfaceOption
10141016
)
10151017

10161018
const (

pkg/tcpip/tests/integration/BUILD

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ go_test(
128128
"//pkg/tcpip/tests/utils",
129129
"//pkg/tcpip/testutil",
130130
"//pkg/tcpip/transport/icmp",
131-
"//pkg/tcpip/transport/raw",
132131
"//pkg/tcpip/transport/udp",
133132
"//pkg/waiter",
134133
"@com_github_google_go_cmp//cmp:go_default_library",

pkg/tcpip/tests/integration/multicast_broadcast_test.go

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ import (
3333
"gvisor.dev/gvisor/pkg/tcpip/tests/utils"
3434
"gvisor.dev/gvisor/pkg/tcpip/testutil"
3535
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
36-
"gvisor.dev/gvisor/pkg/tcpip/transport/raw"
3736
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
3837
"gvisor.dev/gvisor/pkg/waiter"
3938
)
@@ -779,54 +778,3 @@ func TestAddMembershipInterfacePrecedence(t *testing.T) {
779778
t.Fatalf("ep.SetSockOpt(&%#v): %s", addOpt, err)
780779
}
781780
}
782-
783-
func TestMismatchedMulticastAddressAndProtocol(t *testing.T) {
784-
const nicID = 1
785-
// MulticastAddr is IPv4, but proto is IPv6.
786-
multicastAddr := tcpip.AddrFromSlice([]byte("\xe0\x01\x02\x03"))
787-
s := stack.New(stack.Options{
788-
NetworkProtocols: []stack.NetworkProtocolFactory{ipv4.NewProtocol, ipv6.NewProtocol},
789-
TransportProtocols: []stack.TransportProtocolFactory{icmp.NewProtocol6},
790-
RawFactory: raw.EndpointFactory{},
791-
})
792-
e := channel.New(0, defaultMTU, "")
793-
defer e.Close()
794-
if err := s.CreateNIC(nicID, e); err != nil {
795-
t.Fatalf("CreateNIC(%d, _): %s", nicID, err)
796-
}
797-
protoAddr := tcpip.ProtocolAddress{Protocol: header.IPv4ProtocolNumber, AddressWithPrefix: utils.Ipv4Addr}
798-
if err := s.AddProtocolAddress(nicID, protoAddr, stack.AddressProperties{}); err != nil {
799-
t.Fatalf("AddProtocolAddress(%d, %+v, {}): %s", nicID, protoAddr, err)
800-
}
801-
802-
var wq waiter.Queue
803-
ep, err := s.NewRawEndpoint(header.ICMPv6ProtocolNumber, header.IPv6ProtocolNumber, &wq, false)
804-
if err != nil {
805-
t.Fatalf("NewEndpoint(%d, %d, _): %s", udp.ProtocolNumber, header.IPv6ProtocolNumber, err)
806-
}
807-
defer ep.Close()
808-
809-
bindAddr := tcpip.FullAddress{Port: utils.LocalPort}
810-
if err := ep.Bind(bindAddr); err != nil {
811-
t.Fatalf("ep.Bind(%#v): %s", bindAddr, err)
812-
}
813-
814-
memOpt := tcpip.MembershipOption{
815-
MulticastAddr: multicastAddr,
816-
NIC: 0,
817-
InterfaceAddr: utils.Ipv4Addr.Address,
818-
}
819-
820-
// Add/remove membership should succeed when the interface index is specified,
821-
// even if a bad interface address is specified.
822-
addOpt := tcpip.AddMembershipOption(memOpt)
823-
expErr := &tcpip.ErrInvalidOptionValue{}
824-
if err := ep.SetSockOpt(&addOpt); err != expErr {
825-
t.Fatalf("ep.SetSockOpt(&%#v): want %q, got %q", addOpt, expErr, err)
826-
}
827-
828-
removeOpt := tcpip.RemoveMembershipOption(memOpt)
829-
if err := ep.SetSockOpt(&removeOpt); err != expErr {
830-
t.Fatalf("ep.SetSockOpt(&%#v): want %q, got %q", addOpt, expErr, err)
831-
}
832-
}

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

Lines changed: 56 additions & 14 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
@@ -181,7 +182,11 @@ func (e *Endpoint) Close() {
181182
}
182183

183184
for mem := range e.multicastMemberships {
184-
e.stack.LeaveGroup(e.netProto, mem.nicID, mem.multicastAddr)
185+
proto, err := e.multicastNetProto(mem.multicastAddr)
186+
if err != nil {
187+
panic("non multicast address in an existing membership")
188+
}
189+
e.stack.LeaveGroup(proto, mem.nicID, mem.multicastAddr)
185190
}
186191
e.multicastMemberships = nil
187192

@@ -613,14 +618,17 @@ func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, localAddr tcpip.Addres
613618
localAddr = tcpip.Address{}
614619
}
615620

616-
if header.IsV4MulticastAddress(addr.Addr) || header.IsV6MulticastAddress(addr.Addr) {
621+
if header.IsV4MulticastAddress(addr.Addr) {
617622
if nicID == 0 {
618623
nicID = e.multicastNICID
619624
}
620625
if localAddr == (tcpip.Address{}) && nicID == 0 {
621626
localAddr = e.multicastAddr
622627
}
623628
}
629+
if header.IsV6MulticastAddress(addr.Addr) && nicID == 0 {
630+
nicID = e.ipv6MulticastNICID
631+
}
624632
}
625633

626634
// Find a route to the desired destination.
@@ -865,6 +873,18 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
865873
e.mu.Lock()
866874
e.ipv6TClass = uint8(v)
867875
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
868888
}
869889

870890
return nil
@@ -907,11 +927,30 @@ func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
907927
e.mu.RUnlock()
908928
return v, nil
909929

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

941+
// multicastNetProto returns the network protocol of a given multicast address.
942+
// Returns an error if the address is not a multicast address.
943+
func (e *Endpoint) multicastNetProto(addr tcpip.Address) (tcpip.NetworkProtocolNumber, tcpip.Error) {
944+
switch {
945+
case header.IsV4MulticastAddress(addr):
946+
return header.IPv4ProtocolNumber, nil
947+
case header.IsV6MulticastAddress(addr):
948+
return header.IPv6ProtocolNumber, nil
949+
default:
950+
return 0, &tcpip.ErrInvalidOptionValue{}
951+
}
952+
}
953+
915954
// SetSockOpt sets the socket option.
916955
func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
917956
switch v := opt.(type) {
@@ -952,21 +991,23 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
952991
e.multicastAddr = addr
953992

954993
case *tcpip.AddMembershipOption:
955-
if !(header.IsV4MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv4ProtocolNumber) && !(header.IsV6MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv6ProtocolNumber) {
956-
return &tcpip.ErrInvalidOptionValue{}
994+
// Allowing IP_ADD_MEMBERSHIP on an ipv6 socket matches Linux behavior:
995+
// https://github.com/torvalds/linux/blob/cec1e6e5d1a/net/ipv6/ipv6_sockglue.c#L964
996+
proto, err := e.multicastNetProto(v.MulticastAddr)
997+
if err != nil {
998+
return err
957999
}
9581000

9591001
nicID := v.NIC
960-
9611002
if v.InterfaceAddr.Unspecified() {
9621003
if nicID == 0 {
963-
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
1004+
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, proto, false /* multicastLoop */); err == nil {
9641005
nicID = r.NICID()
9651006
r.Release()
9661007
}
9671008
}
9681009
} else {
969-
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
1010+
nicID = e.stack.CheckLocalAddress(nicID, proto, v.InterfaceAddr)
9701011
}
9711012
if nicID == 0 {
9721013
return &tcpip.ErrUnknownDevice{}
@@ -981,27 +1022,28 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
9811022
return &tcpip.ErrPortInUse{}
9821023
}
9831024

984-
if err := e.stack.JoinGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
1025+
if err := e.stack.JoinGroup(proto, nicID, v.MulticastAddr); err != nil {
9851026
return err
9861027
}
9871028

9881029
e.multicastMemberships[memToInsert] = struct{}{}
9891030

9901031
case *tcpip.RemoveMembershipOption:
991-
if !(header.IsV4MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv4ProtocolNumber) && !(header.IsV6MulticastAddress(v.MulticastAddr) && e.netProto == header.IPv6ProtocolNumber) {
992-
return &tcpip.ErrInvalidOptionValue{}
1032+
proto, err := e.multicastNetProto(v.MulticastAddr)
1033+
if err != nil {
1034+
return err
9931035
}
9941036

9951037
nicID := v.NIC
9961038
if v.InterfaceAddr.Unspecified() {
9971039
if nicID == 0 {
998-
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, e.netProto, false /* multicastLoop */); err == nil {
1040+
if r, err := e.stack.FindRoute(0, tcpip.Address{}, v.MulticastAddr, proto, false /* multicastLoop */); err == nil {
9991041
nicID = r.NICID()
10001042
r.Release()
10011043
}
10021044
}
10031045
} else {
1004-
nicID = e.stack.CheckLocalAddress(nicID, e.netProto, v.InterfaceAddr)
1046+
nicID = e.stack.CheckLocalAddress(nicID, proto, v.InterfaceAddr)
10051047
}
10061048
if nicID == 0 {
10071049
return &tcpip.ErrUnknownDevice{}
@@ -1016,7 +1058,7 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
10161058
return &tcpip.ErrBadLocalAddress{}
10171059
}
10181060

1019-
if err := e.stack.LeaveGroup(e.netProto, nicID, v.MulticastAddr); err != nil {
1061+
if err := e.stack.LeaveGroup(proto, nicID, v.MulticastAddr); err != nil {
10201062
return err
10211063
}
10221064

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ func (e *Endpoint) Resume(s *stack.Stack) error {
2929

3030
e.stack = s
3131
for m := range e.multicastMemberships {
32-
if err := e.stack.JoinGroup(e.netProto, m.nicID, m.multicastAddr); err != nil {
33-
return fmt.Errorf("e.stack.JoinGroup(%d, %d, %s): %s", e.netProto, m.nicID, m.multicastAddr, err)
32+
proto, err := e.multicastNetProto(m.multicastAddr)
33+
if err != nil {
34+
return fmt.Errorf("non multicast address in an existing membership during Resume: %s", err)
35+
}
36+
if err := e.stack.JoinGroup(proto, m.nicID, m.multicastAddr); err != nil {
37+
return fmt.Errorf("e.stack.JoinGroup(%d, %d, %s): %s", proto, m.nicID, m.multicastAddr, err)
3438
}
3539
}
3640

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/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,12 @@ syscall_test(
810810
test = "//test/syscalls/linux:socket_ipv4_udp_unbound_external_networking_test",
811811
)
812812

813+
syscall_test(
814+
# TODO(b/446206378): TestJoinLeaveMulticast fails with add_hostinet.
815+
netstack_sr = True,
816+
test = "//test/syscalls/linux:socket_ipv6_udp_unbound_external_networking_test",
817+
)
818+
813819
syscall_test(
814820
size = "large",
815821
add_hostinet = True,

test/syscalls/linux/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2896,7 +2896,13 @@ cc_library(
28962896
"socket_ipv6_udp_unbound_external_networking.h",
28972897
],
28982898
deps = select_gtest() + [
2899+
":ip_socket_test_util",
28992900
":socket_ip_udp_unbound_external_networking",
2901+
"//test/util:file_descriptor",
2902+
"//test/util:posix_error",
2903+
"//test/util:socket_util",
2904+
"//test/util:test_util",
2905+
"@com_google_absl//absl/cleanup",
29002906
],
29012907
alwayslink = 1,
29022908
)

0 commit comments

Comments
 (0)