mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Track join count in multicast group protocol state
Before this change, the join count and the state for IGMP/MLD was held across different types which required multiple locks to be held when accessing a multicast group's state. Bug #4682, #4861 Fixes #4916 PiperOrigin-RevId: 345019091
This commit is contained in:
committed by
gVisor bot
parent
6b1dbbbdc8
commit
25570ac4f3
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -124,7 +124,14 @@ func (igmp *igmpState) init(ep *endpoint, opts IGMPOptions) {
|
||||
defer igmp.mu.Unlock()
|
||||
igmp.ep = ep
|
||||
igmp.opts = opts
|
||||
igmp.mu.genericMulticastProtocol.Init(ep.protocol.stack.Rand(), ep.protocol.stack.Clock(), igmp, UnsolicitedReportIntervalMax)
|
||||
igmp.mu.genericMulticastProtocol.Init(ip.GenericMulticastProtocolOptions{
|
||||
Enabled: opts.Enabled,
|
||||
Rand: ep.protocol.stack.Rand(),
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: igmp,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
AllNodesAddress: header.IPv4AllSystems,
|
||||
})
|
||||
igmp.igmpV1Present = igmpV1PresentDefault
|
||||
igmp.mu.igmpV1Job = igmp.ep.protocol.stack.NewJob(&igmp.mu, func() {
|
||||
igmp.setV1Present(false)
|
||||
@@ -201,17 +208,13 @@ func (igmp *igmpState) setV1Present(v bool) {
|
||||
}
|
||||
|
||||
func (igmp *igmpState) handleMembershipQuery(groupAddress tcpip.Address, maxRespTime time.Duration) {
|
||||
if !igmp.opts.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
igmp.mu.Lock()
|
||||
defer igmp.mu.Unlock()
|
||||
|
||||
// As per RFC 2236 Section 6, Page 10: If the maximum response time is zero
|
||||
// then change the state to note that an IGMPv1 router is present and
|
||||
// schedule the query received Job.
|
||||
if maxRespTime == 0 {
|
||||
if maxRespTime == 0 && igmp.opts.Enabled {
|
||||
igmp.mu.igmpV1Job.Cancel()
|
||||
igmp.mu.igmpV1Job.Schedule(v1RouterPresentTimeout)
|
||||
igmp.setV1Present(true)
|
||||
@@ -222,10 +225,6 @@ func (igmp *igmpState) handleMembershipQuery(groupAddress tcpip.Address, maxResp
|
||||
}
|
||||
|
||||
func (igmp *igmpState) handleMembershipReport(groupAddress tcpip.Address) {
|
||||
if !igmp.opts.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
igmp.mu.Lock()
|
||||
defer igmp.mu.Unlock()
|
||||
igmp.mu.genericMulticastProtocol.HandleReport(groupAddress)
|
||||
@@ -279,49 +278,46 @@ func (igmp *igmpState) writePacket(destAddress tcpip.Address, groupAddress tcpip
|
||||
//
|
||||
// If the group already exists in the membership map, returns
|
||||
// tcpip.ErrDuplicateAddress.
|
||||
func (igmp *igmpState) joinGroup(groupAddress tcpip.Address) *tcpip.Error {
|
||||
if !igmp.opts.Enabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// As per RFC 2236 section 6 page 10,
|
||||
//
|
||||
// The all-systems group (address 224.0.0.1) is handled as a special
|
||||
// case. The host starts in Idle Member state for that group on every
|
||||
// interface, never transitions to another state, and never sends a
|
||||
// report for that group.
|
||||
//
|
||||
// This is equivalent to not performing IGMP for the all-systems multicast
|
||||
// address. Simply not performing IGMP when the group is added will prevent
|
||||
// any work from being done on the all-systems multicast group when leaving
|
||||
// the group or when query or report messages are received for it since the
|
||||
// MGP state will not know about it.
|
||||
if groupAddress == header.IPv4AllSystems {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (igmp *igmpState) joinGroup(groupAddress tcpip.Address) {
|
||||
igmp.mu.Lock()
|
||||
defer igmp.mu.Unlock()
|
||||
igmp.mu.genericMulticastProtocol.JoinGroup(groupAddress, !igmp.ep.Enabled() /* dontInitialize */)
|
||||
}
|
||||
|
||||
// JoinGroup returns false if we have already joined the group.
|
||||
if !igmp.mu.genericMulticastProtocol.JoinGroup(groupAddress) {
|
||||
return tcpip.ErrDuplicateAddress
|
||||
}
|
||||
return nil
|
||||
// isInGroup returns true if the specified group has been joined locally.
|
||||
func (igmp *igmpState) isInGroup(groupAddress tcpip.Address) bool {
|
||||
igmp.mu.Lock()
|
||||
defer igmp.mu.Unlock()
|
||||
return igmp.mu.genericMulticastProtocol.IsLocallyJoined(groupAddress)
|
||||
}
|
||||
|
||||
// leaveGroup handles removing the group from the membership map, cancels any
|
||||
// delay timers associated with that group, and sends the Leave Group message
|
||||
// if required.
|
||||
//
|
||||
// If the group does not exist in the membership map, this function will
|
||||
// silently return.
|
||||
func (igmp *igmpState) leaveGroup(groupAddress tcpip.Address) {
|
||||
if !igmp.opts.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
func (igmp *igmpState) leaveGroup(groupAddress tcpip.Address) *tcpip.Error {
|
||||
igmp.mu.Lock()
|
||||
defer igmp.mu.Unlock()
|
||||
igmp.mu.genericMulticastProtocol.LeaveGroup(groupAddress)
|
||||
|
||||
// LeaveGroup returns false only if the group was not joined.
|
||||
if igmp.mu.genericMulticastProtocol.LeaveGroup(groupAddress) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return tcpip.ErrBadLocalAddress
|
||||
}
|
||||
|
||||
// softLeaveAll leaves all groups from the perspective of IGMP, but remains
|
||||
// joined locally.
|
||||
func (igmp *igmpState) softLeaveAll() {
|
||||
igmp.mu.Lock()
|
||||
defer igmp.mu.Unlock()
|
||||
igmp.mu.genericMulticastProtocol.MakeAllNonMember()
|
||||
}
|
||||
|
||||
// initializeAll attemps to initialize the IGMP state for each group that has
|
||||
// been joined locally.
|
||||
func (igmp *igmpState) initializeAll() {
|
||||
igmp.mu.Lock()
|
||||
defer igmp.mu.Unlock()
|
||||
igmp.mu.genericMulticastProtocol.InitializeGroups()
|
||||
}
|
||||
|
||||
@@ -127,16 +127,18 @@ func (e *endpoint) Enable() *tcpip.Error {
|
||||
// endpoint may have left groups from the perspective of IGMP when the
|
||||
// endpoint was disabled. Either way, we need to let routers know to
|
||||
// send us multicast traffic.
|
||||
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
|
||||
for _, group := range joinedGroups {
|
||||
e.igmp.joinGroup(group)
|
||||
}
|
||||
e.igmp.initializeAll()
|
||||
|
||||
// As per RFC 1122 section 3.3.7, all hosts should join the all-hosts
|
||||
// multicast group. Note, the IANA calls the all-hosts multicast group the
|
||||
// all-systems multicast group.
|
||||
_, err = e.joinGroupLocked(header.IPv4AllSystems)
|
||||
return err
|
||||
if err := e.joinGroupLocked(header.IPv4AllSystems); err != nil {
|
||||
// joinGroupLocked only returns an error if the group address is not a valid
|
||||
// IPv4 multicast address.
|
||||
panic(fmt.Sprintf("e.joinGroupLocked(%s): %s", header.IPv4AllSystems, err))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enabled implements stack.NetworkEndpoint.
|
||||
@@ -173,16 +175,13 @@ func (e *endpoint) disableLocked() {
|
||||
}
|
||||
|
||||
// The endpoint may have already left the multicast group.
|
||||
if _, err := e.leaveGroupLocked(header.IPv4AllSystems); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
if err := e.leaveGroupLocked(header.IPv4AllSystems); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
panic(fmt.Sprintf("unexpected error when leaving group = %s: %s", header.IPv4AllSystems, err))
|
||||
}
|
||||
|
||||
// Leave groups from the perspective of IGMP so that routers know that
|
||||
// we are no longer interested in the group.
|
||||
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
|
||||
for _, group := range joinedGroups {
|
||||
e.igmp.leaveGroup(group)
|
||||
}
|
||||
e.igmp.softLeaveAll()
|
||||
|
||||
// The address may have already been removed.
|
||||
if err := e.mu.addressableEndpointState.RemovePermanentAddress(ipv4BroadcastAddr.Address); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
@@ -849,69 +848,43 @@ func (e *endpoint) PermanentAddresses() []tcpip.AddressWithPrefix {
|
||||
}
|
||||
|
||||
// JoinGroup implements stack.GroupAddressableEndpoint.
|
||||
func (e *endpoint) JoinGroup(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
func (e *endpoint) JoinGroup(addr tcpip.Address) *tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.joinGroupLocked(addr)
|
||||
}
|
||||
|
||||
// joinGroupLocked is like JoinGroup, but with locking requirements.
|
||||
// joinGroupLocked is like JoinGroup but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be locked.
|
||||
func (e *endpoint) joinGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
func (e *endpoint) joinGroupLocked(addr tcpip.Address) *tcpip.Error {
|
||||
if !header.IsV4MulticastAddress(addr) {
|
||||
return false, tcpip.ErrBadAddress
|
||||
}
|
||||
// TODO(gvisor.dev/issue/4916): Keep track of join count and IGMP state in a
|
||||
// single type.
|
||||
joined, err := e.mu.addressableEndpointState.JoinGroup(addr)
|
||||
if err != nil || !joined {
|
||||
return joined, err
|
||||
return tcpip.ErrBadAddress
|
||||
}
|
||||
|
||||
// Only join the group from the perspective of IGMP when the endpoint is
|
||||
// enabled.
|
||||
//
|
||||
// If we are not enabled right now, we will join the group from the
|
||||
// perspective of IGMP when the endpoint is enabled.
|
||||
if !e.Enabled() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// joinGroup only returns an error if we try to join a group twice, but we
|
||||
// checked above to make sure that the group was newly joined.
|
||||
if err := e.igmp.joinGroup(addr); err != nil {
|
||||
panic(fmt.Sprintf("e.igmp.joinGroup(%s): %s", addr, err))
|
||||
}
|
||||
|
||||
return true, nil
|
||||
e.igmp.joinGroup(addr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LeaveGroup implements stack.GroupAddressableEndpoint.
|
||||
func (e *endpoint) LeaveGroup(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
func (e *endpoint) LeaveGroup(addr tcpip.Address) *tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.leaveGroupLocked(addr)
|
||||
}
|
||||
|
||||
// leaveGroupLocked is like LeaveGroup, but with locking requirements.
|
||||
// leaveGroupLocked is like LeaveGroup but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be locked.
|
||||
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
left, err := e.mu.addressableEndpointState.LeaveGroup(addr)
|
||||
if err != nil || !left {
|
||||
return left, err
|
||||
}
|
||||
|
||||
e.igmp.leaveGroup(addr)
|
||||
return true, nil
|
||||
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) *tcpip.Error {
|
||||
return e.igmp.leaveGroup(addr)
|
||||
}
|
||||
|
||||
// IsInGroup implements stack.GroupAddressableEndpoint.
|
||||
func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
return e.mu.addressableEndpointState.IsInGroup(addr)
|
||||
return e.igmp.isInGroup(addr)
|
||||
}
|
||||
|
||||
var _ stack.ForwardingNetworkProtocol = (*protocol)(nil)
|
||||
|
||||
@@ -232,10 +232,7 @@ func (e *endpoint) Enable() *tcpip.Error {
|
||||
// endpoint may have left groups from the perspective of MLD when the
|
||||
// endpoint was disabled. Either way, we need to let routers know to
|
||||
// send us multicast traffic.
|
||||
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
|
||||
for _, group := range joinedGroups {
|
||||
e.mld.joinGroup(group)
|
||||
}
|
||||
e.mld.initializeAll()
|
||||
|
||||
// Join the IPv6 All-Nodes Multicast group if the stack is configured to
|
||||
// use IPv6. This is required to ensure that this node properly receives
|
||||
@@ -254,8 +251,10 @@ func (e *endpoint) Enable() *tcpip.Error {
|
||||
// (NDP NS) messages may be sent to the All-Nodes multicast group if the
|
||||
// source address of the NDP NS is the unspecified address, as per RFC 4861
|
||||
// section 7.2.4.
|
||||
if _, err := e.joinGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil {
|
||||
return err
|
||||
if err := e.joinGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil {
|
||||
// joinGroupLocked only returns an error if the group address is not a valid
|
||||
// IPv6 multicast address.
|
||||
panic(fmt.Sprintf("e.joinGroupLocked(%s): %s", header.IPv6AllNodesMulticastAddress, err))
|
||||
}
|
||||
|
||||
// Perform DAD on the all the unicast IPv6 endpoints that are in the permanent
|
||||
@@ -344,16 +343,13 @@ func (e *endpoint) disableLocked() {
|
||||
e.stopDADForPermanentAddressesLocked()
|
||||
|
||||
// The endpoint may have already left the multicast group.
|
||||
if _, err := e.leaveGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
if err := e.leaveGroupLocked(header.IPv6AllNodesMulticastAddress); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
panic(fmt.Sprintf("unexpected error when leaving group = %s: %s", header.IPv6AllNodesMulticastAddress, err))
|
||||
}
|
||||
|
||||
// Leave groups from the perspective of MLD so that routers know that
|
||||
// we are no longer interested in the group.
|
||||
joinedGroups := e.mu.addressableEndpointState.JoinedGroups()
|
||||
for _, group := range joinedGroups {
|
||||
e.mld.leaveGroup(group)
|
||||
}
|
||||
e.mld.softLeaveAll()
|
||||
}
|
||||
|
||||
// stopDADForPermanentAddressesLocked stops DAD for all permaneent addresses.
|
||||
@@ -1182,8 +1178,10 @@ func (e *endpoint) addAndAcquirePermanentAddressLocked(addr tcpip.AddressWithPre
|
||||
}
|
||||
|
||||
snmc := header.SolicitedNodeAddr(addr.Address)
|
||||
if _, err := e.joinGroupLocked(snmc); err != nil {
|
||||
return nil, err
|
||||
if err := e.joinGroupLocked(snmc); err != nil {
|
||||
// joinGroupLocked only returns an error if the group address is not a valid
|
||||
// IPv6 multicast address.
|
||||
panic(fmt.Sprintf("e.joinGroupLocked(%s): %s", snmc, err))
|
||||
}
|
||||
|
||||
addressEndpoint.SetKind(stack.PermanentTentative)
|
||||
@@ -1239,7 +1237,8 @@ func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEn
|
||||
}
|
||||
|
||||
snmc := header.SolicitedNodeAddr(addr.Address)
|
||||
if _, err := e.leaveGroupLocked(snmc); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
// The endpoint may have already left the multicast group.
|
||||
if err := e.leaveGroupLocked(snmc); err != nil && err != tcpip.ErrBadLocalAddress {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1404,70 +1403,43 @@ func (e *endpoint) PermanentAddresses() []tcpip.AddressWithPrefix {
|
||||
}
|
||||
|
||||
// JoinGroup implements stack.GroupAddressableEndpoint.
|
||||
func (e *endpoint) JoinGroup(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
func (e *endpoint) JoinGroup(addr tcpip.Address) *tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.joinGroupLocked(addr)
|
||||
}
|
||||
|
||||
// joinGroupLocked is like JoinGroup, but with locking requirements.
|
||||
// joinGroupLocked is like JoinGroup but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be locked.
|
||||
func (e *endpoint) joinGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
func (e *endpoint) joinGroupLocked(addr tcpip.Address) *tcpip.Error {
|
||||
if !header.IsV6MulticastAddress(addr) {
|
||||
return false, tcpip.ErrBadAddress
|
||||
return tcpip.ErrBadAddress
|
||||
}
|
||||
|
||||
// TODO(gvisor.dev/issue/4916): Keep track of join count and MLD state in a
|
||||
// single type.
|
||||
joined, err := e.mu.addressableEndpointState.JoinGroup(addr)
|
||||
if err != nil || !joined {
|
||||
return joined, err
|
||||
}
|
||||
|
||||
// Only join the group from the perspective of IGMP when the endpoint is
|
||||
// enabled.
|
||||
//
|
||||
// If we are not enabled right now, we will join the group from the
|
||||
// perspective of MLD when the endpoint is enabled.
|
||||
if !e.Enabled() {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// joinGroup only returns an error if we try to join a group twice, but we
|
||||
// checked above to make sure that the group was newly joined.
|
||||
if err := e.mld.joinGroup(addr); err != nil {
|
||||
panic(fmt.Sprintf("e.mld.joinGroup(%s): %s", addr, err))
|
||||
}
|
||||
|
||||
return true, nil
|
||||
e.mld.joinGroup(addr)
|
||||
return nil
|
||||
}
|
||||
|
||||
// LeaveGroup implements stack.GroupAddressableEndpoint.
|
||||
func (e *endpoint) LeaveGroup(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
func (e *endpoint) LeaveGroup(addr tcpip.Address) *tcpip.Error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
return e.leaveGroupLocked(addr)
|
||||
}
|
||||
|
||||
// leaveGroupLocked is like LeaveGroup, but with locking requirements.
|
||||
// leaveGroupLocked is like LeaveGroup but with locking requirements.
|
||||
//
|
||||
// Precondition: e.mu must be locked.
|
||||
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) (bool, *tcpip.Error) {
|
||||
left, err := e.mu.addressableEndpointState.LeaveGroup(addr)
|
||||
if err != nil || !left {
|
||||
return left, err
|
||||
}
|
||||
|
||||
e.mld.leaveGroup(addr)
|
||||
return true, nil
|
||||
func (e *endpoint) leaveGroupLocked(addr tcpip.Address) *tcpip.Error {
|
||||
return e.mld.leaveGroup(addr)
|
||||
}
|
||||
|
||||
// IsInGroup implements stack.GroupAddressableEndpoint.
|
||||
func (e *endpoint) IsInGroup(addr tcpip.Address) bool {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
return e.mu.addressableEndpointState.IsInGroup(addr)
|
||||
return e.mld.isInGroup(addr)
|
||||
}
|
||||
|
||||
var _ stack.ForwardingNetworkProtocol = (*protocol)(nil)
|
||||
|
||||
@@ -50,8 +50,7 @@ var _ ip.MulticastGroupProtocol = (*mldState)(nil)
|
||||
// mldState.init MUST be called to initialize the MLD state.
|
||||
type mldState struct {
|
||||
// The IPv6 endpoint this mldState is for.
|
||||
ep *endpoint
|
||||
opts MLDOptions
|
||||
ep *endpoint
|
||||
|
||||
genericMulticastProtocol ip.GenericMulticastProtocolState
|
||||
}
|
||||
@@ -70,23 +69,21 @@ func (mld *mldState) SendLeave(groupAddress tcpip.Address) *tcpip.Error {
|
||||
// a new mldState.
|
||||
func (mld *mldState) init(ep *endpoint, opts MLDOptions) {
|
||||
mld.ep = ep
|
||||
mld.opts = opts
|
||||
mld.genericMulticastProtocol.Init(ep.protocol.stack.Rand(), ep.protocol.stack.Clock(), mld, UnsolicitedReportIntervalMax)
|
||||
mld.genericMulticastProtocol.Init(ip.GenericMulticastProtocolOptions{
|
||||
Enabled: opts.Enabled,
|
||||
Rand: ep.protocol.stack.Rand(),
|
||||
Clock: ep.protocol.stack.Clock(),
|
||||
Protocol: mld,
|
||||
MaxUnsolicitedReportDelay: UnsolicitedReportIntervalMax,
|
||||
AllNodesAddress: header.IPv6AllNodesMulticastAddress,
|
||||
})
|
||||
}
|
||||
|
||||
func (mld *mldState) handleMulticastListenerQuery(mldHdr header.MLD) {
|
||||
if !mld.opts.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
mld.genericMulticastProtocol.HandleQuery(mldHdr.MulticastAddress(), mldHdr.MaximumResponseDelay())
|
||||
}
|
||||
|
||||
func (mld *mldState) handleMulticastListenerReport(mldHdr header.MLD) {
|
||||
if !mld.opts.Enabled {
|
||||
return
|
||||
}
|
||||
|
||||
mld.genericMulticastProtocol.HandleReport(mldHdr.MulticastAddress())
|
||||
}
|
||||
|
||||
@@ -94,45 +91,37 @@ func (mld *mldState) handleMulticastListenerReport(mldHdr header.MLD) {
|
||||
// messages.
|
||||
//
|
||||
// If the group is already joined, returns tcpip.ErrDuplicateAddress.
|
||||
func (mld *mldState) joinGroup(groupAddress tcpip.Address) *tcpip.Error {
|
||||
if !mld.opts.Enabled {
|
||||
return nil
|
||||
}
|
||||
func (mld *mldState) joinGroup(groupAddress tcpip.Address) {
|
||||
mld.genericMulticastProtocol.JoinGroup(groupAddress, !mld.ep.Enabled() /* dontInitialize */)
|
||||
}
|
||||
|
||||
// As per RFC 2710 section 5 page 10,
|
||||
//
|
||||
// The link-scope all-nodes address (FF02::1) is handled as a special
|
||||
// case. The node starts in Idle Listener state for that address on
|
||||
// every interface, never transitions to another state, and never sends
|
||||
// a Report or Done for that address.
|
||||
//
|
||||
// This is equivalent to not performing MLD for the all-nodes multicast
|
||||
// address. Simply not performing MLD when the group is added will prevent
|
||||
// any work from being done on the all-nodes multicast group when leaving the
|
||||
// group or when query or report messages are received for it since the MGP
|
||||
// state will not know about it.
|
||||
if groupAddress == header.IPv6AllNodesMulticastAddress {
|
||||
return nil
|
||||
}
|
||||
|
||||
// JoinGroup returns false if we have already joined the group.
|
||||
if !mld.genericMulticastProtocol.JoinGroup(groupAddress) {
|
||||
return tcpip.ErrDuplicateAddress
|
||||
}
|
||||
return nil
|
||||
// isInGroup returns true if the specified group has been joined locally.
|
||||
func (mld *mldState) isInGroup(groupAddress tcpip.Address) bool {
|
||||
return mld.genericMulticastProtocol.IsLocallyJoined(groupAddress)
|
||||
}
|
||||
|
||||
// leaveGroup handles removing the group from the membership map, cancels any
|
||||
// delay timers associated with that group, and sends the Done message, if
|
||||
// required.
|
||||
//
|
||||
// If the group is not joined, this function will do nothing.
|
||||
func (mld *mldState) leaveGroup(groupAddress tcpip.Address) {
|
||||
if !mld.opts.Enabled {
|
||||
return
|
||||
func (mld *mldState) leaveGroup(groupAddress tcpip.Address) *tcpip.Error {
|
||||
// LeaveGroup returns false only if the group was not joined.
|
||||
if mld.genericMulticastProtocol.LeaveGroup(groupAddress) {
|
||||
return nil
|
||||
}
|
||||
|
||||
mld.genericMulticastProtocol.LeaveGroup(groupAddress)
|
||||
return tcpip.ErrBadLocalAddress
|
||||
}
|
||||
|
||||
// softLeaveAll leaves all groups from the perspective of MLD, but remains
|
||||
// joined locally.
|
||||
func (mld *mldState) softLeaveAll() {
|
||||
mld.genericMulticastProtocol.MakeAllNonMember()
|
||||
}
|
||||
|
||||
// initializeAll attemps to initialize the MLD state for each group that has
|
||||
// been joined locally.
|
||||
func (mld *mldState) initializeAll() {
|
||||
mld.genericMulticastProtocol.InitializeGroups()
|
||||
}
|
||||
|
||||
func (mld *mldState) writePacket(destAddress, groupAddress tcpip.Address, mldType header.ICMPv6Type) *tcpip.Error {
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
var _ GroupAddressableEndpoint = (*AddressableEndpointState)(nil)
|
||||
var _ AddressableEndpoint = (*AddressableEndpointState)(nil)
|
||||
|
||||
// AddressableEndpointState is an implementation of an AddressableEndpoint.
|
||||
@@ -37,10 +36,6 @@ type AddressableEndpointState struct {
|
||||
|
||||
endpoints map[tcpip.Address]*addressState
|
||||
primary []*addressState
|
||||
|
||||
// groups holds the mapping between group addresses and the number of times
|
||||
// they have been joined.
|
||||
groups map[tcpip.Address]uint32
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +48,6 @@ func (a *AddressableEndpointState) Init(networkEndpoint NetworkEndpoint) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.mu.endpoints = make(map[tcpip.Address]*addressState)
|
||||
a.mu.groups = make(map[tcpip.Address]uint32)
|
||||
}
|
||||
|
||||
// ReadOnlyAddressableEndpointState provides read-only access to an
|
||||
@@ -335,11 +329,6 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
|
||||
func (a *AddressableEndpointState) RemovePermanentAddress(addr tcpip.Address) *tcpip.Error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
if _, ok := a.mu.groups[addr]; ok {
|
||||
panic(fmt.Sprintf("group address = %s must be removed with LeaveGroup", addr))
|
||||
}
|
||||
|
||||
return a.removePermanentAddressLocked(addr)
|
||||
}
|
||||
|
||||
@@ -588,61 +577,11 @@ func (a *AddressableEndpointState) PermanentAddresses() []tcpip.AddressWithPrefi
|
||||
return addrs
|
||||
}
|
||||
|
||||
// JoinGroup implements GroupAddressableEndpoint.
|
||||
func (a *AddressableEndpointState) JoinGroup(group tcpip.Address) (bool, *tcpip.Error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
joins, ok := a.mu.groups[group]
|
||||
a.mu.groups[group] = joins + 1
|
||||
return !ok, nil
|
||||
}
|
||||
|
||||
// LeaveGroup implements GroupAddressableEndpoint.
|
||||
func (a *AddressableEndpointState) LeaveGroup(group tcpip.Address) (bool, *tcpip.Error) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
joins, ok := a.mu.groups[group]
|
||||
if !ok {
|
||||
return false, tcpip.ErrBadLocalAddress
|
||||
}
|
||||
|
||||
if joins == 1 {
|
||||
delete(a.mu.groups, group)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
a.mu.groups[group] = joins - 1
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsInGroup implements GroupAddressableEndpoint.
|
||||
func (a *AddressableEndpointState) IsInGroup(group tcpip.Address) bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
_, ok := a.mu.groups[group]
|
||||
return ok
|
||||
}
|
||||
|
||||
// JoinedGroups returns a list of groups the endpoint is a member of.
|
||||
func (a *AddressableEndpointState) JoinedGroups() []tcpip.Address {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
groups := make([]tcpip.Address, 0, len(a.mu.groups))
|
||||
for g := range a.mu.groups {
|
||||
groups = append(groups, g)
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// Cleanup forcefully leaves all groups and removes all permanent addresses.
|
||||
func (a *AddressableEndpointState) Cleanup() {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
a.mu.groups = make(map[tcpip.Address]uint32)
|
||||
|
||||
for _, ep := range a.mu.endpoints {
|
||||
// removePermanentEndpointLocked returns tcpip.ErrBadLocalAddress if ep is
|
||||
// not a permanent address.
|
||||
|
||||
@@ -15,40 +15,12 @@
|
||||
package stack_test
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
func TestJoinedGroups(t *testing.T) {
|
||||
const addr1 = tcpip.Address("\x01")
|
||||
const addr2 = tcpip.Address("\x02")
|
||||
|
||||
var ep fakeNetworkEndpoint
|
||||
var s stack.AddressableEndpointState
|
||||
s.Init(&ep)
|
||||
|
||||
if joined, err := s.JoinGroup(addr1); err != nil {
|
||||
t.Fatalf("JoinGroup(%s): %s", addr1, err)
|
||||
} else if !joined {
|
||||
t.Errorf("got JoinGroup(%s) = false, want = true", addr1)
|
||||
}
|
||||
if joined, err := s.JoinGroup(addr2); err != nil {
|
||||
t.Fatalf("JoinGroup(%s): %s", addr2, err)
|
||||
} else if !joined {
|
||||
t.Errorf("got JoinGroup(%s) = false, want = true", addr2)
|
||||
}
|
||||
|
||||
joinedGroups := s.JoinedGroups()
|
||||
sort.Slice(joinedGroups, func(i, j int) bool { return joinedGroups[i][0] < joinedGroups[j][0] })
|
||||
if diff := cmp.Diff([]tcpip.Address{addr1, addr2}, joinedGroups); diff != "" {
|
||||
t.Errorf("joined groups mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddressableEndpointStateCleanup tests that cleaning up an addressable
|
||||
// endpoint state removes permanent addresses and leaves groups.
|
||||
func TestAddressableEndpointStateCleanup(t *testing.T) {
|
||||
@@ -81,25 +53,9 @@ func TestAddressableEndpointStateCleanup(t *testing.T) {
|
||||
ep.DecRef()
|
||||
}
|
||||
|
||||
group := tcpip.Address("\x02")
|
||||
if added, err := s.JoinGroup(group); err != nil {
|
||||
t.Fatalf("s.JoinGroup(%s): %s", group, err)
|
||||
} else if !added {
|
||||
t.Fatalf("got s.JoinGroup(%s) = false, want = true", group)
|
||||
}
|
||||
if !s.IsInGroup(group) {
|
||||
t.Fatalf("got s.IsInGroup(%s) = false, want = true", group)
|
||||
}
|
||||
|
||||
s.Cleanup()
|
||||
{
|
||||
ep := s.AcquireAssignedAddress(addr.Address, false /* allowTemp */, stack.NeverPrimaryEndpoint)
|
||||
if ep != nil {
|
||||
ep.DecRef()
|
||||
t.Fatalf("got s.AcquireAssignedAddress(%s, false, NeverPrimaryEndpoint) = %s, want = nil", addr.Address, ep.AddressWithPrefix())
|
||||
}
|
||||
}
|
||||
if s.IsInGroup(group) {
|
||||
t.Fatalf("got s.IsInGroup(%s) = true, want = false", group)
|
||||
if ep := s.AcquireAssignedAddress(addr.Address, false /* allowTemp */, stack.NeverPrimaryEndpoint); ep != nil {
|
||||
ep.DecRef()
|
||||
t.Fatalf("got s.AcquireAssignedAddress(%s, false, NeverPrimaryEndpoint) = %s, want = nil", addr.Address, ep.AddressWithPrefix())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,8 +563,7 @@ func (n *NIC) joinGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Address
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
_, err := gep.JoinGroup(addr)
|
||||
return err
|
||||
return gep.JoinGroup(addr)
|
||||
}
|
||||
|
||||
// leaveGroup decrements the count for the given multicast address, and when it
|
||||
@@ -580,11 +579,7 @@ func (n *NIC) leaveGroup(protocol tcpip.NetworkProtocolNumber, addr tcpip.Addres
|
||||
return tcpip.ErrNotSupported
|
||||
}
|
||||
|
||||
if _, err := gep.LeaveGroup(addr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
return gep.LeaveGroup(addr)
|
||||
}
|
||||
|
||||
// isInGroup returns true if n has joined the multicast group addr.
|
||||
|
||||
@@ -291,14 +291,10 @@ type NetworkHeaderParams struct {
|
||||
// endpoints may associate themselves with the same identifier (group address).
|
||||
type GroupAddressableEndpoint interface {
|
||||
// JoinGroup joins the specified group.
|
||||
//
|
||||
// Returns true if the group was newly joined.
|
||||
JoinGroup(group tcpip.Address) (bool, *tcpip.Error)
|
||||
JoinGroup(group tcpip.Address) *tcpip.Error
|
||||
|
||||
// LeaveGroup attempts to leave the specified group.
|
||||
//
|
||||
// Returns tcpip.ErrBadLocalAddress if the endpoint has not joined the group.
|
||||
LeaveGroup(group tcpip.Address) (bool, *tcpip.Error)
|
||||
LeaveGroup(group tcpip.Address) *tcpip.Error
|
||||
|
||||
// IsInGroup returns true if the endpoint is a member of the specified group.
|
||||
IsInGroup(group tcpip.Address) bool
|
||||
|
||||
Reference in New Issue
Block a user