github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/tcpip/network/internal/ip/generic_multicast_protocol.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ip
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"time"
    21  
    22  	"github.com/metacubex/gvisor/pkg/sync"
    23  	"github.com/metacubex/gvisor/pkg/tcpip"
    24  	"github.com/metacubex/gvisor/pkg/tcpip/header"
    25  )
    26  
    27  const (
    28  	// As per RFC 2236 section 3,
    29  	//
    30  	//   When a host joins a multicast group, it should immediately transmit
    31  	//   an unsolicited Version 2 Membership Report for that group, in case it
    32  	//   is the first member of that group on the network.  To cover the
    33  	//   possibility of the initial Membership Report being lost or damaged,
    34  	//   it is recommended that it be repeated once or twice after short
    35  	//   delays [Unsolicited Report Interval].  (A simple way to accomplish
    36  	//   this is to send the initial Version 2 Membership Report and then act
    37  	//   as if a Group-Specific Query was received for that group, and set a
    38  	//   timer appropriately).
    39  	//
    40  	// As per RFC 2710 section 4,
    41  	//
    42  	//   When a node starts listening to a multicast address on an interface,
    43  	//   it should immediately transmit an unsolicited Report for that address
    44  	//   on that interface, in case it is the first listener on the link.  To
    45  	//   cover the possibility of the initial Report being lost or damaged, it
    46  	//   is recommended that it be repeated once or twice after short delays
    47  	//   [Unsolicited Report Interval].  (A simple way to accomplish this is
    48  	//   to send the initial Report and then act as if a Multicast-Address-
    49  	//   Specific Query was received for that address, and set a timer
    50  	//   appropriately).
    51  	unsolicitedTransmissionCount = 2
    52  
    53  	// Responses to queries may be delayed, but we only send a response to a
    54  	// query once. A response to a query can be handled by any pending
    55  	// unsolicited transmission count, but we should send at least one report
    56  	// after sending a query.
    57  	//
    58  	// As per RFC 2236 section 3,
    59  	//
    60  	//   When a host receives a General Query, it sets delay timers for each
    61  	//   group (excluding the all-systems group) of which it is a member on
    62  	//   the interface from which it received the query.
    63  	//
    64  	// As per RFC 2710 section 4,
    65  	//
    66  	//   When a node receives a General Query, it sets a delay timer for each
    67  	//   multicast address to which it is listening on the interface from
    68  	//   which it received the Query, EXCLUDING the link-scope all-nodes
    69  	//   address and any multicast addresses of scope 0 (reserved) or 1
    70  	//   (node-local).
    71  	minQueryResponseTransmissionCount = 1
    72  
    73  	// DefaultRobustnessVariable is the default robustness variable
    74  	//
    75  	// As per RFC 3810 section 9.1 (for MLDv2),
    76  	//
    77  	//   The Robustness Variable allows tuning for the expected packet loss on
    78  	//   a link.  If a link is expected to be lossy, the value of the
    79  	//   Robustness Variable may be increased.  MLD is robust to [Robustness
    80  	//   Variable] - 1 packet losses.  The value of the Robustness Variable
    81  	//   MUST NOT be zero, and SHOULD NOT be one.  Default value: 2.
    82  	//
    83  	// As per RFC 3376 section 8.1 (for IGMPv3),
    84  	//
    85  	//   The Robustness Variable allows tuning for the expected packet loss on
    86  	//   a network.  If a network is expected to be lossy, the Robustness
    87  	//   Variable may be increased.  IGMP is robust to (Robustness Variable -
    88  	//   1) packet losses.  The Robustness Variable MUST NOT be zero, and
    89  	//   SHOULD NOT be one.  Default: 2
    90  	DefaultRobustnessVariable = 2
    91  
    92  	// DefaultQueryInterval is the default query interval.
    93  	//
    94  	// As per RFC 3810 section 9.2 (for MLDv2),
    95  	//
    96  	//   The Query Interval variable denotes the interval between General
    97  	//   Queries sent by the Querier.  Default value: 125 seconds.
    98  	//
    99  	// As per RFC 3376 section 8.2 (for IGMPv3),
   100  	//
   101  	//   The Query Interval is the interval between General Queries sent by
   102  	//   the Querier.  Default: 125 seconds.
   103  	DefaultQueryInterval = 125 * time.Second
   104  )
   105  
   106  // multicastGroupState holds the Generic Multicast Protocol state for a
   107  // multicast group.
   108  type multicastGroupState struct {
   109  	// joins is the number of times the group has been joined.
   110  	joins uint64
   111  
   112  	// transmissionLeft is the number of transmissions left to send.
   113  	transmissionLeft uint8
   114  
   115  	// lastToSendReport is true if we sent the last report for the group. It is
   116  	// used to track whether there are other hosts on the subnet that are also
   117  	// members of the group.
   118  	//
   119  	// Defined in RFC 2236 section 6 page 9 for IGMPv2 and RFC 2710 section 5 page
   120  	// 8 for MLDv1.
   121  	lastToSendReport bool
   122  
   123  	// delayedReportJob is used to delay sending responses to membership report
   124  	// messages in order to reduce duplicate reports from multiple hosts on the
   125  	// interface.
   126  	//
   127  	// Must not be nil.
   128  	delayedReportJob *tcpip.Job
   129  
   130  	// delyedReportJobFiresAt is the time when the delayed report job will fire.
   131  	//
   132  	// A zero value indicates that the job is not scheduled.
   133  	delayedReportJobFiresAt time.Time
   134  
   135  	// queriedIncludeSources holds sources that were queried for.
   136  	//
   137  	// Indicates that there is a pending source-specific query response for the
   138  	// multicast address.
   139  	queriedIncludeSources map[tcpip.Address]struct{}
   140  
   141  	deleteScheduled bool
   142  }
   143  
   144  func (m *multicastGroupState) cancelDelayedReportJob() {
   145  	m.delayedReportJob.Cancel()
   146  	m.delayedReportJobFiresAt = time.Time{}
   147  	m.transmissionLeft = 0
   148  }
   149  
   150  func (m *multicastGroupState) clearQueriedIncludeSources() {
   151  	for source := range m.queriedIncludeSources {
   152  		delete(m.queriedIncludeSources, source)
   153  	}
   154  }
   155  
   156  // GenericMulticastProtocolOptions holds options for the generic multicast
   157  // protocol.
   158  type GenericMulticastProtocolOptions struct {
   159  	// Rand is the source of random numbers.
   160  	Rand *rand.Rand
   161  
   162  	// Clock is the clock used to create timers.
   163  	Clock tcpip.Clock
   164  
   165  	// Protocol is the implementation of the variant of multicast group protocol
   166  	// in use.
   167  	Protocol MulticastGroupProtocol
   168  
   169  	// MaxUnsolicitedReportDelay is the maximum amount of time to wait between
   170  	// transmitting unsolicited reports.
   171  	//
   172  	// Unsolicited reports are transmitted when a group is newly joined.
   173  	MaxUnsolicitedReportDelay time.Duration
   174  }
   175  
   176  // MulticastGroupProtocolV2ReportRecordType is the type of a
   177  // MulticastGroupProtocolv2 multicast address record.
   178  type MulticastGroupProtocolV2ReportRecordType int
   179  
   180  // MulticastGroupProtocolv2 multicast address record types.
   181  const (
   182  	_ MulticastGroupProtocolV2ReportRecordType = iota
   183  	MulticastGroupProtocolV2ReportRecordModeIsInclude
   184  	MulticastGroupProtocolV2ReportRecordModeIsExclude
   185  	MulticastGroupProtocolV2ReportRecordChangeToIncludeMode
   186  	MulticastGroupProtocolV2ReportRecordChangeToExcludeMode
   187  	MulticastGroupProtocolV2ReportRecordAllowNewSources
   188  	MulticastGroupProtocolV2ReportRecordBlockOldSources
   189  )
   190  
   191  // MulticastGroupProtocolV2ReportBuilder is a builder for a V2 report.
   192  type MulticastGroupProtocolV2ReportBuilder interface {
   193  	// AddRecord adds a record to the report.
   194  	AddRecord(recordType MulticastGroupProtocolV2ReportRecordType, groupAddress tcpip.Address)
   195  
   196  	// Send sends the report.
   197  	//
   198  	// Does nothing if no records were added.
   199  	//
   200  	// It is invalid to use this builder after this method is called.
   201  	Send() (sent bool, err tcpip.Error)
   202  }
   203  
   204  // MulticastGroupProtocol is a multicast group protocol whose core state machine
   205  // can be represented by GenericMulticastProtocolState.
   206  type MulticastGroupProtocol interface {
   207  	// Enabled indicates whether the generic multicast protocol will be
   208  	// performed.
   209  	//
   210  	// When enabled, the protocol may transmit report and leave messages when
   211  	// joining and leaving multicast groups respectively, and handle incoming
   212  	// packets.
   213  	//
   214  	// When disabled, the protocol will still keep track of locally joined groups,
   215  	// it just won't transmit and handle packets, or update groups' state.
   216  	Enabled() bool
   217  
   218  	// SendReport sends a multicast report for the specified group address.
   219  	//
   220  	// Returns false if the caller should queue the report to be sent later. Note,
   221  	// returning false does not mean that the receiver hit an error.
   222  	SendReport(groupAddress tcpip.Address) (sent bool, err tcpip.Error)
   223  
   224  	// SendLeave sends a multicast leave for the specified group address.
   225  	SendLeave(groupAddress tcpip.Address) tcpip.Error
   226  
   227  	// ShouldPerformProtocol returns true iff the protocol should be performed for
   228  	// the specified group.
   229  	ShouldPerformProtocol(tcpip.Address) bool
   230  
   231  	// NewReportV2Builder creates a new V2 builder.
   232  	NewReportV2Builder() MulticastGroupProtocolV2ReportBuilder
   233  
   234  	// V2QueryMaxRespCodeToV2Delay takes a V2 query's maximum response code and
   235  	// returns the V2 delay.
   236  	V2QueryMaxRespCodeToV2Delay(code uint16) time.Duration
   237  
   238  	// V2QueryMaxRespCodeToV1Delay takes a V2 query's maximum response code and
   239  	// returns the V1 delay.
   240  	V2QueryMaxRespCodeToV1Delay(code uint16) time.Duration
   241  }
   242  
   243  type protocolMode int
   244  
   245  const (
   246  	protocolModeV2 protocolMode = iota
   247  	protocolModeV1
   248  	protocolModeV1Compatibility
   249  )
   250  
   251  // GenericMulticastProtocolState is the per interface generic multicast protocol
   252  // state.
   253  //
   254  // There is actually no protocol named "Generic Multicast Protocol". Instead,
   255  // the term used to refer to a generic multicast protocol that applies to both
   256  // IPv4 and IPv6. Specifically, Generic Multicast Protocol is the core state
   257  // machine of IGMPv2 as defined by RFC 2236 and MLDv1 as defined by RFC 2710.
   258  //
   259  // Callers must synchronize accesses to the generic multicast protocol state;
   260  // GenericMulticastProtocolState obtains no locks in any of its methods. The
   261  // only exception to this is GenericMulticastProtocolState's timer/job callbacks
   262  // which will obtain the lock provided to the GenericMulticastProtocolState when
   263  // it is initialized.
   264  //
   265  // GenericMulticastProtocolState.Init MUST be called before calling any of
   266  // the methods on GenericMulticastProtocolState.
   267  //
   268  // GenericMulticastProtocolState.MakeAllNonMemberLocked MUST be called when the
   269  // multicast group protocol is disabled so that leave messages may be sent.
   270  type GenericMulticastProtocolState struct {
   271  	// Do not allow overwriting this state.
   272  	_ sync.NoCopy
   273  
   274  	opts GenericMulticastProtocolOptions
   275  
   276  	// memberships holds group addresses and their associated state.
   277  	memberships map[tcpip.Address]multicastGroupState
   278  
   279  	// protocolMU is the mutex used to protect the protocol.
   280  	protocolMU *sync.RWMutex
   281  
   282  	// V2 state.
   283  	robustnessVariable uint8
   284  	queryInterval      time.Duration
   285  	mode               protocolMode
   286  	modeTimer          tcpip.Timer
   287  
   288  	generalQueryV2Timer        tcpip.Timer
   289  	generalQueryV2TimerFiresAt time.Time
   290  
   291  	stateChangedReportV2Timer    tcpip.Timer
   292  	stateChangedReportV2TimerSet bool
   293  }
   294  
   295  // GetV1ModeLocked returns the V1 configuration.
   296  //
   297  // Precondition: g.protocolMU must be read locked.
   298  func (g *GenericMulticastProtocolState) GetV1ModeLocked() bool {
   299  	switch g.mode {
   300  	case protocolModeV2, protocolModeV1Compatibility:
   301  		return false
   302  	case protocolModeV1:
   303  		return true
   304  	default:
   305  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   306  	}
   307  }
   308  
   309  func (g *GenericMulticastProtocolState) stopModeTimer() {
   310  	if g.modeTimer != nil {
   311  		g.modeTimer.Stop()
   312  	}
   313  }
   314  
   315  // SetV1ModeLocked sets the V1 configuration.
   316  //
   317  // Returns the previous configuration.
   318  //
   319  // Precondition: g.protocolMU must be locked.
   320  func (g *GenericMulticastProtocolState) SetV1ModeLocked(v bool) bool {
   321  	if g.GetV1ModeLocked() == v {
   322  		return v
   323  	}
   324  
   325  	if v {
   326  		g.stopModeTimer()
   327  		g.cancelV2ReportTimers()
   328  		g.mode = protocolModeV1
   329  		return false
   330  	}
   331  
   332  	g.mode = protocolModeV2
   333  	return true
   334  }
   335  
   336  func (g *GenericMulticastProtocolState) cancelV2ReportTimers() {
   337  	if g.generalQueryV2Timer != nil {
   338  		g.generalQueryV2Timer.Stop()
   339  		g.generalQueryV2TimerFiresAt = time.Time{}
   340  	}
   341  
   342  	if g.stateChangedReportV2Timer != nil {
   343  		g.stateChangedReportV2Timer.Stop()
   344  		g.stateChangedReportV2TimerSet = false
   345  	}
   346  }
   347  
   348  // Init initializes the Generic Multicast Protocol state.
   349  //
   350  // Must only be called once for the lifetime of g; Init will panic if it is
   351  // called twice.
   352  //
   353  // The GenericMulticastProtocolState will only grab the lock when timers/jobs
   354  // fire.
   355  //
   356  // Note: the methods on opts.Protocol will always be called while protocolMU is
   357  // held.
   358  func (g *GenericMulticastProtocolState) Init(protocolMU *sync.RWMutex, opts GenericMulticastProtocolOptions) {
   359  	if g.memberships != nil {
   360  		panic("attempted to initialize generic membership protocol state twice")
   361  	}
   362  
   363  	*g = GenericMulticastProtocolState{
   364  		opts:               opts,
   365  		memberships:        make(map[tcpip.Address]multicastGroupState),
   366  		protocolMU:         protocolMU,
   367  		robustnessVariable: DefaultRobustnessVariable,
   368  		queryInterval:      DefaultQueryInterval,
   369  		mode:               protocolModeV2,
   370  	}
   371  }
   372  
   373  // MakeAllNonMemberLocked transitions all groups to the non-member state.
   374  //
   375  // The groups will still be considered joined locally.
   376  //
   377  // MUST be called when the multicast group protocol is disabled.
   378  //
   379  // Precondition: g.protocolMU must be locked.
   380  func (g *GenericMulticastProtocolState) MakeAllNonMemberLocked() {
   381  	if !g.opts.Protocol.Enabled() {
   382  		return
   383  	}
   384  
   385  	g.stopModeTimer()
   386  	g.cancelV2ReportTimers()
   387  
   388  	var v2ReportBuilder MulticastGroupProtocolV2ReportBuilder
   389  	var handler func(tcpip.Address, *multicastGroupState)
   390  	switch g.mode {
   391  	case protocolModeV2:
   392  		v2ReportBuilder = g.opts.Protocol.NewReportV2Builder()
   393  		handler = func(groupAddress tcpip.Address, info *multicastGroupState) {
   394  			info.cancelDelayedReportJob()
   395  
   396  			// Send a report immediately to announce us leaving the group.
   397  			v2ReportBuilder.AddRecord(
   398  				MulticastGroupProtocolV2ReportRecordChangeToIncludeMode,
   399  				groupAddress,
   400  			)
   401  		}
   402  	case protocolModeV1Compatibility:
   403  		g.mode = protocolModeV2
   404  		fallthrough
   405  	case protocolModeV1:
   406  		handler = g.transitionToNonMemberLocked
   407  	default:
   408  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   409  	}
   410  
   411  	for groupAddress, info := range g.memberships {
   412  		if !g.shouldPerformForGroup(groupAddress) {
   413  			continue
   414  		}
   415  
   416  		handler(groupAddress, &info)
   417  
   418  		if info.deleteScheduled {
   419  			delete(g.memberships, groupAddress)
   420  		} else {
   421  			info.transmissionLeft = 0
   422  			g.memberships[groupAddress] = info
   423  		}
   424  	}
   425  
   426  	if v2ReportBuilder != nil {
   427  		// Nothing meaningful we can do with the error here - this method may be
   428  		// called when an interface is being disabled when we expect sends to
   429  		// fail.
   430  		_, _ = v2ReportBuilder.Send()
   431  	}
   432  }
   433  
   434  // InitializeGroupsLocked initializes each group, as if they were newly joined
   435  // but without affecting the groups' join count.
   436  //
   437  // Must only be called after calling MakeAllNonMember as a group should not be
   438  // initialized while it is not in the non-member state.
   439  //
   440  // Precondition: g.protocolMU must be locked.
   441  func (g *GenericMulticastProtocolState) InitializeGroupsLocked() {
   442  	if !g.opts.Protocol.Enabled() {
   443  		return
   444  	}
   445  
   446  	var v2ReportBuilder MulticastGroupProtocolV2ReportBuilder
   447  	switch g.mode {
   448  	case protocolModeV2:
   449  		v2ReportBuilder = g.opts.Protocol.NewReportV2Builder()
   450  	case protocolModeV1Compatibility, protocolModeV1:
   451  	default:
   452  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   453  	}
   454  
   455  	for groupAddress, info := range g.memberships {
   456  		g.initializeNewMemberLocked(groupAddress, &info, v2ReportBuilder)
   457  		g.memberships[groupAddress] = info
   458  	}
   459  
   460  	if v2ReportBuilder == nil {
   461  		return
   462  	}
   463  
   464  	if sent, err := v2ReportBuilder.Send(); sent && err == nil {
   465  		g.scheduleStateChangedTimer()
   466  	} else {
   467  		// Nothing meaningful we could do with the error here - the interface may
   468  		// not yet have an address. This is okay because we would either schedule a
   469  		// report to be sent later or we will be notified when an address is added,
   470  		// at which point we will try to send messages again.
   471  		for groupAddress, info := range g.memberships {
   472  			if !g.shouldPerformForGroup(groupAddress) {
   473  				continue
   474  			}
   475  
   476  			// Revert the transmissions count since we did not successfully send.
   477  			info.transmissionLeft++
   478  			g.memberships[groupAddress] = info
   479  		}
   480  	}
   481  }
   482  
   483  // SendQueuedReportsLocked attempts to send reports for groups that failed to
   484  // send reports during their last attempt.
   485  //
   486  // Precondition: g.protocolMU must be locked.
   487  func (g *GenericMulticastProtocolState) SendQueuedReportsLocked() {
   488  	if g.stateChangedReportV2TimerSet {
   489  		return
   490  	}
   491  
   492  	for groupAddress, info := range g.memberships {
   493  		if info.delayedReportJobFiresAt.IsZero() {
   494  			switch g.mode {
   495  			case protocolModeV2:
   496  				g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode)
   497  			case protocolModeV1Compatibility, protocolModeV1:
   498  				g.maybeSendReportLocked(groupAddress, &info)
   499  			default:
   500  				panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   501  			}
   502  
   503  			g.memberships[groupAddress] = info
   504  		}
   505  	}
   506  }
   507  
   508  // JoinGroupLocked handles joining a new group.
   509  //
   510  // Precondition: g.protocolMU must be locked.
   511  func (g *GenericMulticastProtocolState) JoinGroupLocked(groupAddress tcpip.Address) {
   512  	info, ok := g.memberships[groupAddress]
   513  	if ok {
   514  		info.joins++
   515  		if info.joins > 1 {
   516  			// The group has already been joined.
   517  			g.memberships[groupAddress] = info
   518  			return
   519  		}
   520  	} else {
   521  		info = multicastGroupState{
   522  			// Since we just joined the group, its count is 1.
   523  			joins:            1,
   524  			lastToSendReport: false,
   525  			delayedReportJob: tcpip.NewJob(g.opts.Clock, g.protocolMU, func() {
   526  				if !g.opts.Protocol.Enabled() {
   527  					panic(fmt.Sprintf("delayed report job fired for group %s while the multicast group protocol is disabled", groupAddress))
   528  				}
   529  
   530  				info, ok := g.memberships[groupAddress]
   531  				if !ok {
   532  					panic(fmt.Sprintf("expected to find group state for group = %s", groupAddress))
   533  				}
   534  
   535  				info.delayedReportJobFiresAt = time.Time{}
   536  
   537  				switch g.mode {
   538  				case protocolModeV2:
   539  					reportBuilder := g.opts.Protocol.NewReportV2Builder()
   540  					reportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordModeIsExclude, groupAddress)
   541  					// Nothing meaningful we can do with the error here - we only try to
   542  					// send a delayed report once.
   543  					_, _ = reportBuilder.Send()
   544  				case protocolModeV1Compatibility, protocolModeV1:
   545  					g.maybeSendReportLocked(groupAddress, &info)
   546  				default:
   547  					panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   548  				}
   549  
   550  				info.clearQueriedIncludeSources()
   551  				g.memberships[groupAddress] = info
   552  			}),
   553  			queriedIncludeSources: make(map[tcpip.Address]struct{}),
   554  		}
   555  	}
   556  
   557  	info.deleteScheduled = false
   558  	info.clearQueriedIncludeSources()
   559  	info.delayedReportJobFiresAt = time.Time{}
   560  	info.lastToSendReport = false
   561  	g.initializeNewMemberLocked(groupAddress, &info, nil /* callersV2ReportBuilder */)
   562  	g.memberships[groupAddress] = info
   563  }
   564  
   565  // IsLocallyJoinedRLocked returns true if the group is locally joined.
   566  //
   567  // Precondition: g.protocolMU must be read locked.
   568  func (g *GenericMulticastProtocolState) IsLocallyJoinedRLocked(groupAddress tcpip.Address) bool {
   569  	info, ok := g.memberships[groupAddress]
   570  	return ok && !info.deleteScheduled
   571  }
   572  
   573  func (g *GenericMulticastProtocolState) sendV2ReportAndMaybeScheduleChangedTimer(
   574  	groupAddress tcpip.Address,
   575  	info *multicastGroupState,
   576  	recordType MulticastGroupProtocolV2ReportRecordType,
   577  ) bool {
   578  	if info.transmissionLeft == 0 {
   579  		return false
   580  	}
   581  
   582  	successfullySentAndHasMore := false
   583  
   584  	// Send a report immediately to announce us leaving the group.
   585  	reportBuilder := g.opts.Protocol.NewReportV2Builder()
   586  	reportBuilder.AddRecord(recordType, groupAddress)
   587  	if sent, err := reportBuilder.Send(); sent && err == nil {
   588  		info.transmissionLeft--
   589  
   590  		successfullySentAndHasMore = info.transmissionLeft != 0
   591  
   592  		// Use the interface-wide state changed report for further transmissions.
   593  		if successfullySentAndHasMore {
   594  			g.scheduleStateChangedTimer()
   595  		}
   596  	}
   597  
   598  	return successfullySentAndHasMore
   599  }
   600  
   601  func (g *GenericMulticastProtocolState) scheduleStateChangedTimer() {
   602  	if g.stateChangedReportV2TimerSet {
   603  		return
   604  	}
   605  
   606  	delay := g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay)
   607  	if g.stateChangedReportV2Timer == nil {
   608  		// TODO(https://issuetracker.google.com/264799098): Create timer on
   609  		// initialization instead of lazily creating the timer since the timer
   610  		// does not change after being created.
   611  		g.stateChangedReportV2Timer = g.opts.Clock.AfterFunc(delay, func() {
   612  			g.protocolMU.Lock()
   613  			defer g.protocolMU.Unlock()
   614  
   615  			reportBuilder := g.opts.Protocol.NewReportV2Builder()
   616  			nonEmptyReport := false
   617  			for groupAddress, info := range g.memberships {
   618  				if info.transmissionLeft == 0 || !g.shouldPerformForGroup(groupAddress) {
   619  					continue
   620  				}
   621  
   622  				info.transmissionLeft--
   623  				nonEmptyReport = true
   624  
   625  				mode := MulticastGroupProtocolV2ReportRecordChangeToExcludeMode
   626  				if info.deleteScheduled {
   627  					mode = MulticastGroupProtocolV2ReportRecordChangeToIncludeMode
   628  				}
   629  				reportBuilder.AddRecord(mode, groupAddress)
   630  
   631  				if info.deleteScheduled && info.transmissionLeft == 0 {
   632  					// No more transmissions left so we can actually delete the
   633  					// membership.
   634  					delete(g.memberships, groupAddress)
   635  				} else {
   636  					g.memberships[groupAddress] = info
   637  				}
   638  			}
   639  
   640  			// Nothing meaningful we can do with the error here. We will retry
   641  			// sending a state changed report again anyways.
   642  			_, _ = reportBuilder.Send()
   643  
   644  			if nonEmptyReport {
   645  				g.stateChangedReportV2Timer.Reset(g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay))
   646  			} else {
   647  				g.stateChangedReportV2TimerSet = false
   648  			}
   649  		})
   650  	} else {
   651  		g.stateChangedReportV2Timer.Reset(delay)
   652  	}
   653  	g.stateChangedReportV2TimerSet = true
   654  }
   655  
   656  // LeaveGroupLocked handles leaving the group.
   657  //
   658  // Returns false if the group is not currently joined.
   659  //
   660  // Precondition: g.protocolMU must be locked.
   661  func (g *GenericMulticastProtocolState) LeaveGroupLocked(groupAddress tcpip.Address) bool {
   662  	info, ok := g.memberships[groupAddress]
   663  	if !ok || info.joins == 0 {
   664  		return false
   665  	}
   666  
   667  	info.joins--
   668  	if info.joins != 0 {
   669  		// If we still have outstanding joins, then do nothing further.
   670  		g.memberships[groupAddress] = info
   671  		return true
   672  	}
   673  
   674  	info.deleteScheduled = true
   675  	info.cancelDelayedReportJob()
   676  
   677  	if !g.shouldPerformForGroup(groupAddress) {
   678  		delete(g.memberships, groupAddress)
   679  		return true
   680  	}
   681  
   682  	switch g.mode {
   683  	case protocolModeV2:
   684  		info.transmissionLeft = g.robustnessVariable
   685  		if g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToIncludeMode) {
   686  			g.memberships[groupAddress] = info
   687  		} else {
   688  			delete(g.memberships, groupAddress)
   689  		}
   690  	case protocolModeV1Compatibility, protocolModeV1:
   691  		g.transitionToNonMemberLocked(groupAddress, &info)
   692  		delete(g.memberships, groupAddress)
   693  	default:
   694  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   695  	}
   696  
   697  	return true
   698  }
   699  
   700  // HandleQueryV2Locked handles a V2 query.
   701  //
   702  // Precondition: g.protocolMU must be locked.
   703  func (g *GenericMulticastProtocolState) HandleQueryV2Locked(groupAddress tcpip.Address, maxResponseCode uint16, sources header.AddressIterator, robustnessVariable uint8, queryInterval time.Duration) {
   704  	if !g.opts.Protocol.Enabled() {
   705  		return
   706  	}
   707  
   708  	switch g.mode {
   709  	case protocolModeV1Compatibility, protocolModeV1:
   710  		g.handleQueryInnerLocked(groupAddress, g.opts.Protocol.V2QueryMaxRespCodeToV1Delay(maxResponseCode))
   711  		return
   712  	case protocolModeV2:
   713  	default:
   714  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   715  	}
   716  
   717  	if robustnessVariable != 0 {
   718  		g.robustnessVariable = robustnessVariable
   719  	}
   720  
   721  	if queryInterval != 0 {
   722  		g.queryInterval = queryInterval
   723  	}
   724  
   725  	maxResponseTime := g.calculateDelayTimerDuration(g.opts.Protocol.V2QueryMaxRespCodeToV2Delay(maxResponseCode))
   726  
   727  	// As per RFC 3376 section 5.2,
   728  	//
   729  	//   1. If there is a pending response to a previous General Query
   730  	//      scheduled sooner than the selected delay, no additional response
   731  	//      needs to be scheduled.
   732  	//
   733  	//   2. If the received Query is a General Query, the interface timer is
   734  	//      used to schedule a response to the General Query after the
   735  	//      selected delay.  Any previously pending response to a General
   736  	//      Query is canceled.
   737  	//
   738  	//   3. If the received Query is a Group-Specific Query or a Group-and-
   739  	//      Source-Specific Query and there is no pending response to a
   740  	//      previous Query for this group, then the group timer is used to
   741  	//      schedule a report.  If the received Query is a Group-and-Source-
   742  	//      Specific Query, the list of queried sources is recorded to be used
   743  	//      when generating a response.
   744  	//
   745  	//   4. If there already is a pending response to a previous Query
   746  	//      scheduled for this group, and either the new Query is a Group-
   747  	//      Specific Query or the recorded source-list associated with the
   748  	//      group is empty, then the group source-list is cleared and a single
   749  	//      response is scheduled using the group timer.  The new response is
   750  	//      scheduled to be sent at the earliest of the remaining time for the
   751  	//      pending report and the selected delay.
   752  	//
   753  	//   5. If the received Query is a Group-and-Source-Specific Query and
   754  	//      there is a pending response for this group with a non-empty
   755  	//      source-list, then the group source list is augmented to contain
   756  	//      the list of sources in the new Query and a single response is
   757  	//      scheduled using the group timer.  The new response is scheduled to
   758  	//      be sent at the earliest of the remaining time for the pending
   759  	//      report and the selected delay.
   760  	//
   761  	// As per RFC 3810 section 6.2,
   762  	//
   763  	//   1. If there is a pending response to a previous General Query
   764  	//      scheduled sooner than the selected delay, no additional response
   765  	//      needs to be scheduled.
   766  	//
   767  	//   2. If the received Query is a General Query, the Interface Timer is
   768  	//      used to schedule a response to the General Query after the
   769  	//      selected delay.  Any previously pending response to a General
   770  	//      Query is canceled.
   771  	//
   772  	//   3. If the received Query is a Multicast Address Specific Query or a
   773  	//      Multicast Address and Source Specific Query and there is no
   774  	//      pending response to a previous Query for this multicast address,
   775  	//      then the Multicast Address Timer is used to schedule a report.  If
   776  	//      the received Query is a Multicast Address and Source Specific
   777  	//      Query, the list of queried sources is recorded to be used when
   778  	//      generating a response.
   779  	//
   780  	//   4. If there is already a pending response to a previous Query
   781  	//      scheduled for this multicast address, and either the new Query is
   782  	//      a Multicast Address Specific Query or the recorded source list
   783  	//      associated with the multicast address is empty, then the multicast
   784  	//      address source list is cleared and a single response is scheduled,
   785  	//      using the Multicast Address Timer.  The new response is scheduled
   786  	//      to be sent at the earliest of the remaining time for the pending
   787  	//      report and the selected delay.
   788  	//
   789  	//   5. If the received Query is a Multicast Address and Source Specific
   790  	//      Query and there is a pending response for this multicast address
   791  	//      with a non-empty source list, then the multicast address source
   792  	//      list is augmented to contain the list of sources in the new Query,
   793  	//      and a single response is scheduled using the Multicast Address
   794  	//      Timer.  The new response is scheduled to be sent at the earliest
   795  	//      of the remaining time for the pending report and the selected
   796  	//      delay.
   797  	now := g.opts.Clock.Now()
   798  	if !g.generalQueryV2TimerFiresAt.IsZero() && g.generalQueryV2TimerFiresAt.Sub(now) <= maxResponseTime {
   799  		return
   800  	}
   801  
   802  	if groupAddress.Unspecified() {
   803  		if g.generalQueryV2Timer == nil {
   804  			// TODO(https://issuetracker.google.com/264799098): Create timer on
   805  			// initialization instead of lazily creating the timer since the timer
   806  			// does not change after being created.
   807  			g.generalQueryV2Timer = g.opts.Clock.AfterFunc(maxResponseTime, func() {
   808  				g.protocolMU.Lock()
   809  				defer g.protocolMU.Unlock()
   810  
   811  				g.generalQueryV2TimerFiresAt = time.Time{}
   812  
   813  				// As per RFC 3810 section 6.3,
   814  				//
   815  				//      If the expired timer is the Interface Timer (i.e., there is a
   816  				//      pending response to a General Query), then one Current State
   817  				//      Record is sent for each multicast address for which the specified
   818  				//      interface has listening state, as described in section 4.2.  The
   819  				//      Current State Record carries the multicast address and its
   820  				//      associated filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and
   821  				//      Source list.  Multiple Current State Records are packed into
   822  				//      individual Report messages, to the extent possible.
   823  				//
   824  				// As per RFC 3376 section 5.2,
   825  				//
   826  				//      If the expired timer is the interface timer (i.e., it is a pending
   827  				//      response to a General Query), then one Current-State Record is
   828  				//      sent for each multicast address for which the specified interface
   829  				//      has reception state, as described in section 3.2.  The Current-
   830  				//      State Record carries the multicast address and its associated
   831  				//      filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and source list.
   832  				//      Multiple Current-State Records are packed into individual Report
   833  				//      messages, to the extent possible.
   834  				reportBuilder := g.opts.Protocol.NewReportV2Builder()
   835  				for groupAddress, info := range g.memberships {
   836  					if info.deleteScheduled || !g.shouldPerformForGroup(groupAddress) {
   837  						continue
   838  					}
   839  
   840  					// A MODE_IS_EXCLUDE record without any sources indicates that we are
   841  					// interested in traffic from all sources for the group.
   842  					//
   843  					// We currently only hold groups if we have an active interest in the
   844  					// group.
   845  					reportBuilder.AddRecord(
   846  						MulticastGroupProtocolV2ReportRecordModeIsExclude,
   847  						groupAddress,
   848  					)
   849  				}
   850  
   851  				_, _ = reportBuilder.Send()
   852  			})
   853  		} else {
   854  			g.generalQueryV2Timer.Reset(maxResponseTime)
   855  		}
   856  		g.generalQueryV2TimerFiresAt = now.Add(maxResponseTime)
   857  		return
   858  	}
   859  
   860  	if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled && g.shouldPerformForGroup(groupAddress) {
   861  		if info.delayedReportJobFiresAt.IsZero() || (!sources.Done() && len(info.queriedIncludeSources) != 0) {
   862  			for {
   863  				source, ok := sources.Next()
   864  				if !ok {
   865  					break
   866  				}
   867  
   868  				info.queriedIncludeSources[source] = struct{}{}
   869  			}
   870  		} else {
   871  			info.clearQueriedIncludeSources()
   872  		}
   873  		g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime)
   874  		g.memberships[groupAddress] = info
   875  	}
   876  }
   877  
   878  // HandleQueryLocked handles a query message with the specified maximum response
   879  // time.
   880  //
   881  // If the group address is unspecified, then reports will be scheduled for all
   882  // joined groups.
   883  //
   884  // Report(s) will be scheduled to be sent after a random duration between 0 and
   885  // the maximum response time.
   886  //
   887  // Precondition: g.protocolMU must be locked.
   888  func (g *GenericMulticastProtocolState) HandleQueryLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) {
   889  	if !g.opts.Protocol.Enabled() {
   890  		return
   891  	}
   892  
   893  	switch g.mode {
   894  	case protocolModeV2, protocolModeV1Compatibility:
   895  		// As per 3376 section 8.12 (for IGMPv3),
   896  		//
   897  		//   The Older Version Querier Interval is the time-out for transitioning
   898  		//   a host back to IGMPv3 mode once an older version query is heard.
   899  		//   When an older version query is received, hosts set their Older
   900  		//   Version Querier Present Timer to Older Version Querier Interval.
   901  		//
   902  		//   This value MUST be ((the Robustness Variable) times (the Query
   903  		//   Interval in the last Query received)) plus (one Query Response
   904  		//   Interval).
   905  		//
   906  		// As per RFC 3810 section 9.12 (for MLDv2),
   907  		//
   908  		//   The Older Version Querier Present Timeout is the time-out for
   909  		//   transitioning a host back to MLDv2 Host Compatibility Mode.  When an
   910  		//   MLDv1 query is received, MLDv2 hosts set their Older Version Querier
   911  		//   Present Timer to [Older Version Querier Present Timeout].
   912  		//
   913  		//   This value MUST be ([Robustness Variable] times (the [Query Interval]
   914  		//   in the last Query received)) plus ([Query Response Interval]).
   915  		modeRevertDelay := time.Duration(g.robustnessVariable) * g.queryInterval
   916  		if g.modeTimer == nil {
   917  			// TODO(https://issuetracker.google.com/264799098): Create timer on
   918  			// initialization instead of lazily creating the timer since the timer
   919  			// does not change after being created.
   920  			g.modeTimer = g.opts.Clock.AfterFunc(modeRevertDelay, func() {
   921  				g.protocolMU.Lock()
   922  				defer g.protocolMU.Unlock()
   923  				g.mode = protocolModeV2
   924  			})
   925  		} else {
   926  			g.modeTimer.Reset(modeRevertDelay)
   927  		}
   928  		g.mode = protocolModeV1Compatibility
   929  		g.cancelV2ReportTimers()
   930  	case protocolModeV1:
   931  	default:
   932  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   933  	}
   934  	g.handleQueryInnerLocked(groupAddress, maxResponseTime)
   935  }
   936  
   937  func (g *GenericMulticastProtocolState) handleQueryInnerLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) {
   938  	maxResponseTime = g.calculateDelayTimerDuration(maxResponseTime)
   939  
   940  	// As per RFC 2236 section 2.4 (for IGMPv2),
   941  	//
   942  	//   In a Membership Query message, the group address field is set to zero
   943  	//   when sending a General Query, and set to the group address being
   944  	//   queried when sending a Group-Specific Query.
   945  	//
   946  	// As per RFC 2710 section 3.6 (for MLDv1),
   947  	//
   948  	//   In a Query message, the Multicast Address field is set to zero when
   949  	//   sending a General Query, and set to a specific IPv6 multicast address
   950  	//   when sending a Multicast-Address-Specific Query.
   951  	if groupAddress.Unspecified() {
   952  		// This is a general query as the group address is unspecified.
   953  		for groupAddress, info := range g.memberships {
   954  			g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime)
   955  			g.memberships[groupAddress] = info
   956  		}
   957  	} else if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled {
   958  		g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime)
   959  		g.memberships[groupAddress] = info
   960  	}
   961  }
   962  
   963  // HandleReportLocked handles a report message.
   964  //
   965  // If the report is for a joined group, any active delayed report will be
   966  // cancelled and the host state for the group transitions to idle.
   967  //
   968  // Precondition: g.protocolMU must be locked.
   969  func (g *GenericMulticastProtocolState) HandleReportLocked(groupAddress tcpip.Address) {
   970  	if !g.opts.Protocol.Enabled() {
   971  		return
   972  	}
   973  
   974  	// As per RFC 2236 section 3 pages 3-4 (for IGMPv2),
   975  	//
   976  	//   If the host receives another host's Report (version 1 or 2) while it has
   977  	//   a timer running, it stops its timer for the specified group and does not
   978  	//   send a Report
   979  	//
   980  	// As per RFC 2710 section 4 page 6 (for MLDv1),
   981  	//
   982  	//   If a node receives another node's Report from an interface for a
   983  	//   multicast address while it has a timer running for that same address
   984  	//   on that interface, it stops its timer and does not send a Report for
   985  	//   that address, thus suppressing duplicate reports on the link.
   986  	if info, ok := g.memberships[groupAddress]; ok {
   987  		info.cancelDelayedReportJob()
   988  		info.lastToSendReport = false
   989  		g.memberships[groupAddress] = info
   990  	}
   991  }
   992  
   993  // initializeNewMemberLocked initializes a new group membership.
   994  //
   995  // Precondition: g.protocolMU must be locked.
   996  func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress tcpip.Address, info *multicastGroupState, callersV2ReportBuilder MulticastGroupProtocolV2ReportBuilder) {
   997  	if !g.shouldPerformForGroup(groupAddress) {
   998  		return
   999  	}
  1000  
  1001  	info.lastToSendReport = false
  1002  
  1003  	switch g.mode {
  1004  	case protocolModeV2:
  1005  		info.transmissionLeft = g.robustnessVariable
  1006  		if callersV2ReportBuilder == nil {
  1007  			g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode)
  1008  		} else {
  1009  			callersV2ReportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordChangeToExcludeMode, groupAddress)
  1010  			info.transmissionLeft--
  1011  		}
  1012  	case protocolModeV1Compatibility, protocolModeV1:
  1013  		info.transmissionLeft = unsolicitedTransmissionCount
  1014  		g.maybeSendReportLocked(groupAddress, info)
  1015  	default:
  1016  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
  1017  	}
  1018  }
  1019  
  1020  func (g *GenericMulticastProtocolState) shouldPerformForGroup(groupAddress tcpip.Address) bool {
  1021  	return g.opts.Protocol.ShouldPerformProtocol(groupAddress) && g.opts.Protocol.Enabled()
  1022  }
  1023  
  1024  // maybeSendReportLocked attempts to send a report for a group.
  1025  //
  1026  // Precondition: g.protocolMU must be locked.
  1027  func (g *GenericMulticastProtocolState) maybeSendReportLocked(groupAddress tcpip.Address, info *multicastGroupState) {
  1028  	if info.transmissionLeft == 0 {
  1029  		return
  1030  	}
  1031  
  1032  	// As per RFC 2236 section 3 page 5 (for IGMPv2),
  1033  	//
  1034  	//   When a host joins a multicast group, it should immediately transmit an
  1035  	//   unsolicited Version 2 Membership Report for that group" ... "it is
  1036  	//   recommended that it be repeated".
  1037  	//
  1038  	// As per RFC 2710 section 4 page 6 (for MLDv1),
  1039  	//
  1040  	//   When a node starts listening to a multicast address on an interface,
  1041  	//   it should immediately transmit an unsolicited Report for that address
  1042  	//   on that interface, in case it is the first listener on the link. To
  1043  	//   cover the possibility of the initial Report being lost or damaged, it
  1044  	//   is recommended that it be repeated once or twice after short delays
  1045  	//   [Unsolicited Report Interval].
  1046  	//
  1047  	// TODO(gvisor.dev/issue/4901): Support a configurable number of initial
  1048  	// unsolicited reports.
  1049  	sent, err := g.opts.Protocol.SendReport(groupAddress)
  1050  	if err == nil && sent {
  1051  		info.lastToSendReport = true
  1052  
  1053  		info.transmissionLeft--
  1054  		if info.transmissionLeft > 0 {
  1055  			g.setDelayTimerForAddressLocked(
  1056  				groupAddress,
  1057  				info,
  1058  				g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay),
  1059  			)
  1060  		}
  1061  	}
  1062  }
  1063  
  1064  // maybeSendLeave attempts to send a leave message.
  1065  func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Address, lastToSendReport bool) {
  1066  	if !g.shouldPerformForGroup(groupAddress) || !lastToSendReport {
  1067  		return
  1068  	}
  1069  
  1070  	// Okay to ignore the error here as if packet write failed, the multicast
  1071  	// routers will eventually drop our membership anyways. If the interface is
  1072  	// being disabled or removed, the generic multicast protocol's should be
  1073  	// cleared eventually.
  1074  	//
  1075  	// As per RFC 2236 section 3 page 5 (for IGMPv2),
  1076  	//
  1077  	//   When a router receives a Report, it adds the group being reported to
  1078  	//   the list of multicast group memberships on the network on which it
  1079  	//   received the Report and sets the timer for the membership to the
  1080  	//   [Group Membership Interval]. Repeated Reports refresh the timer. If
  1081  	//   no Reports are received for a particular group before this timer has
  1082  	//   expired, the router assumes that the group has no local members and
  1083  	//   that it need not forward remotely-originated multicasts for that
  1084  	//   group onto the attached network.
  1085  	//
  1086  	// As per RFC 2710 section 4 page 5 (for MLDv1),
  1087  	//
  1088  	//   When a router receives a Report from a link, if the reported address
  1089  	//   is not already present in the router's list of multicast address
  1090  	//   having listeners on that link, the reported address is added to the
  1091  	//   list, its timer is set to [Multicast Listener Interval], and its
  1092  	//   appearance is made known to the router's multicast routing component.
  1093  	//   If a Report is received for a multicast address that is already
  1094  	//   present in the router's list, the timer for that address is reset to
  1095  	//   [Multicast Listener Interval]. If an address's timer expires, it is
  1096  	//   assumed that there are no longer any listeners for that address
  1097  	//   present on the link, so it is deleted from the list and its
  1098  	//   disappearance is made known to the multicast routing component.
  1099  	//
  1100  	// The requirement to send a leave message is also optional (it MAY be
  1101  	// skipped):
  1102  	//
  1103  	// As per RFC 2236 section 6 page 8 (for IGMPv2),
  1104  	//
  1105  	//  "send leave" for the group on the interface. If the interface
  1106  	//   state says the Querier is running IGMPv1, this action SHOULD be
  1107  	//   skipped. If the flag saying we were the last host to report is
  1108  	//   cleared, this action MAY be skipped. The Leave Message is sent to
  1109  	//   the ALL-ROUTERS group (224.0.0.2).
  1110  	//
  1111  	// As per RFC 2710 section 5 page 8 (for MLDv1),
  1112  	//
  1113  	//   "send done" for the address on the interface. If the flag saying
  1114  	//   we were the last node to report is cleared, this action MAY be
  1115  	//   skipped. The Done message is sent to the link-scope all-routers
  1116  	//   address (FF02::2).
  1117  	_ = g.opts.Protocol.SendLeave(groupAddress)
  1118  }
  1119  
  1120  // transitionToNonMemberLocked transitions the given multicast group the the
  1121  // non-member/listener state.
  1122  //
  1123  // Precondition: g.protocolMU must be locked.
  1124  func (g *GenericMulticastProtocolState) transitionToNonMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) {
  1125  	info.cancelDelayedReportJob()
  1126  	g.maybeSendLeave(groupAddress, info.lastToSendReport)
  1127  	info.lastToSendReport = false
  1128  }
  1129  
  1130  // setDelayTimerForAddressLocked sets timer to send a delayed report.
  1131  //
  1132  // Precondition: g.protocolMU MUST be locked.
  1133  func (g *GenericMulticastProtocolState) setDelayTimerForAddressLocked(groupAddress tcpip.Address, info *multicastGroupState, maxResponseTime time.Duration) {
  1134  	if !g.shouldPerformForGroup(groupAddress) {
  1135  		return
  1136  	}
  1137  
  1138  	if info.transmissionLeft < minQueryResponseTransmissionCount {
  1139  		info.transmissionLeft = minQueryResponseTransmissionCount
  1140  	}
  1141  
  1142  	// As per RFC 2236 section 3 page 3 (for IGMPv2),
  1143  	//
  1144  	//   If a timer for the group is already running, it is reset to the random
  1145  	//   value only if the requested Max Response Time is less than the remaining
  1146  	//   value of the running timer.
  1147  	//
  1148  	// As per RFC 2710 section 4 page 5 (for MLDv1),
  1149  	//
  1150  	//   If a timer for any address is already running, it is reset to the new
  1151  	//   random value only if the requested Maximum Response Delay is less than
  1152  	//   the remaining value of the running timer.
  1153  	now := g.opts.Clock.Now()
  1154  	if !info.delayedReportJobFiresAt.IsZero() && info.delayedReportJobFiresAt.Sub(now) <= maxResponseTime {
  1155  		// The timer is scheduled to fire before the maximum response time so we
  1156  		// leave our timer as is.
  1157  		return
  1158  	}
  1159  
  1160  	info.delayedReportJob.Cancel()
  1161  	info.delayedReportJob.Schedule(maxResponseTime)
  1162  	info.delayedReportJobFiresAt = now.Add(maxResponseTime)
  1163  }
  1164  
  1165  // calculateDelayTimerDuration returns a random time between (0, maxRespTime].
  1166  func (g *GenericMulticastProtocolState) calculateDelayTimerDuration(maxRespTime time.Duration) time.Duration {
  1167  	// As per RFC 2236 section 3 page 3 (for IGMPv2),
  1168  	//
  1169  	//   When a host receives a Group-Specific Query, it sets a delay timer to a
  1170  	//   random value selected from the range (0, Max Response Time]...
  1171  	//
  1172  	// As per RFC 2710 section 4 page 6 (for MLDv1),
  1173  	//
  1174  	//   When a node receives a Multicast-Address-Specific Query, if it is
  1175  	//   listening to the queried Multicast Address on the interface from
  1176  	//   which the Query was received, it sets a delay timer for that address
  1177  	//   to a random value selected from the range [0, Maximum Response Delay],
  1178  	//   as above.
  1179  	if maxRespTime == 0 {
  1180  		return 0
  1181  	}
  1182  	return time.Duration(g.opts.Rand.Int63n(int64(maxRespTime)))
  1183  }