@@ -66,10 +66,11 @@ type Endpoint struct {
66
66
// TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
67
67
// +checklocks:mu
68
68
multicastAddr tcpip.Address
69
- // TODO(https://gvisor.dev/issue/6389): Use different fields for IPv4/IPv6.
70
69
// +checklocks:mu
71
70
multicastNICID tcpip.NICID
72
71
// +checklocks:mu
72
+ ipv6MulticastNICID tcpip.NICID
73
+ // +checklocks:mu
73
74
ipv4TOS uint8
74
75
// +checklocks:mu
75
76
ipv6TClass uint8
@@ -181,7 +182,11 @@ func (e *Endpoint) Close() {
181
182
}
182
183
183
184
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 )
185
190
}
186
191
e .multicastMemberships = nil
187
192
@@ -613,14 +618,17 @@ func (e *Endpoint) connectRouteRLocked(nicID tcpip.NICID, localAddr tcpip.Addres
613
618
localAddr = tcpip.Address {}
614
619
}
615
620
616
- if header .IsV4MulticastAddress (addr .Addr ) || header . IsV6MulticastAddress ( addr . Addr ) {
621
+ if header .IsV4MulticastAddress (addr .Addr ) {
617
622
if nicID == 0 {
618
623
nicID = e .multicastNICID
619
624
}
620
625
if localAddr == (tcpip.Address {}) && nicID == 0 {
621
626
localAddr = e .multicastAddr
622
627
}
623
628
}
629
+ if header .IsV6MulticastAddress (addr .Addr ) && nicID == 0 {
630
+ nicID = e .ipv6MulticastNICID
631
+ }
624
632
}
625
633
626
634
// Find a route to the desired destination.
@@ -865,6 +873,18 @@ func (e *Endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
865
873
e .mu .Lock ()
866
874
e .ipv6TClass = uint8 (v )
867
875
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
868
888
}
869
889
870
890
return nil
@@ -907,11 +927,30 @@ func (e *Endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
907
927
e .mu .RUnlock ()
908
928
return v , nil
909
929
930
+ case tcpip .IPv6MulticastInterfaceOption :
931
+ e .mu .RLock ()
932
+ v := int (e .ipv6MulticastNICID )
933
+ e .mu .RUnlock ()
934
+ return v , nil
935
+
910
936
default :
911
937
return - 1 , & tcpip.ErrUnknownProtocolOption {}
912
938
}
913
939
}
914
940
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
+
915
954
// SetSockOpt sets the socket option.
916
955
func (e * Endpoint ) SetSockOpt (opt tcpip.SettableSocketOption ) tcpip.Error {
917
956
switch v := opt .(type ) {
@@ -952,21 +991,23 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
952
991
e .multicastAddr = addr
953
992
954
993
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
957
999
}
958
1000
959
1001
nicID := v .NIC
960
-
961
1002
if v .InterfaceAddr .Unspecified () {
962
1003
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 {
964
1005
nicID = r .NICID ()
965
1006
r .Release ()
966
1007
}
967
1008
}
968
1009
} else {
969
- nicID = e .stack .CheckLocalAddress (nicID , e . netProto , v .InterfaceAddr )
1010
+ nicID = e .stack .CheckLocalAddress (nicID , proto , v .InterfaceAddr )
970
1011
}
971
1012
if nicID == 0 {
972
1013
return & tcpip.ErrUnknownDevice {}
@@ -981,27 +1022,28 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
981
1022
return & tcpip.ErrPortInUse {}
982
1023
}
983
1024
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 {
985
1026
return err
986
1027
}
987
1028
988
1029
e .multicastMemberships [memToInsert ] = struct {}{}
989
1030
990
1031
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
993
1035
}
994
1036
995
1037
nicID := v .NIC
996
1038
if v .InterfaceAddr .Unspecified () {
997
1039
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 {
999
1041
nicID = r .NICID ()
1000
1042
r .Release ()
1001
1043
}
1002
1044
}
1003
1045
} else {
1004
- nicID = e .stack .CheckLocalAddress (nicID , e . netProto , v .InterfaceAddr )
1046
+ nicID = e .stack .CheckLocalAddress (nicID , proto , v .InterfaceAddr )
1005
1047
}
1006
1048
if nicID == 0 {
1007
1049
return & tcpip.ErrUnknownDevice {}
@@ -1016,7 +1058,7 @@ func (e *Endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
1016
1058
return & tcpip.ErrBadLocalAddress {}
1017
1059
}
1018
1060
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 {
1020
1062
return err
1021
1063
}
1022
1064
0 commit comments