github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/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/MerlinKodo/gvisor/pkg/sync"
    23  	"github.com/MerlinKodo/gvisor/pkg/tcpip"
    24  	"github.com/MerlinKodo/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, _ *multicastGroupState) {
   394  			// Send a report immediately to announce us leaving the group.
   395  			v2ReportBuilder.AddRecord(
   396  				MulticastGroupProtocolV2ReportRecordChangeToIncludeMode,
   397  				groupAddress,
   398  			)
   399  		}
   400  	case protocolModeV1Compatibility:
   401  		g.mode = protocolModeV2
   402  		fallthrough
   403  	case protocolModeV1:
   404  		handler = g.transitionToNonMemberLocked
   405  	default:
   406  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   407  	}
   408  
   409  	for groupAddress, info := range g.memberships {
   410  		if !g.shouldPerformForGroup(groupAddress) {
   411  			continue
   412  		}
   413  
   414  		handler(groupAddress, &info)
   415  
   416  		if info.deleteScheduled {
   417  			delete(g.memberships, groupAddress)
   418  		} else {
   419  			info.transmissionLeft = 0
   420  			g.memberships[groupAddress] = info
   421  		}
   422  	}
   423  
   424  	if v2ReportBuilder != nil {
   425  		// Nothing meaningful we can do with the error here - this method may be
   426  		// called when an interface is being disabled when we expect sends to
   427  		// fail.
   428  		_, _ = v2ReportBuilder.Send()
   429  	}
   430  }
   431  
   432  // InitializeGroupsLocked initializes each group, as if they were newly joined
   433  // but without affecting the groups' join count.
   434  //
   435  // Must only be called after calling MakeAllNonMember as a group should not be
   436  // initialized while it is not in the non-member state.
   437  //
   438  // Precondition: g.protocolMU must be locked.
   439  func (g *GenericMulticastProtocolState) InitializeGroupsLocked() {
   440  	if !g.opts.Protocol.Enabled() {
   441  		return
   442  	}
   443  
   444  	var v2ReportBuilder MulticastGroupProtocolV2ReportBuilder
   445  	switch g.mode {
   446  	case protocolModeV2:
   447  		v2ReportBuilder = g.opts.Protocol.NewReportV2Builder()
   448  	case protocolModeV1Compatibility, protocolModeV1:
   449  	default:
   450  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   451  	}
   452  
   453  	for groupAddress, info := range g.memberships {
   454  		g.initializeNewMemberLocked(groupAddress, &info, v2ReportBuilder)
   455  		g.memberships[groupAddress] = info
   456  	}
   457  
   458  	if v2ReportBuilder == nil {
   459  		return
   460  	}
   461  
   462  	if sent, err := v2ReportBuilder.Send(); sent && err == nil {
   463  		g.scheduleStateChangedTimer()
   464  	} else {
   465  		// Nothing meaningful we could do with the error here - the interface may
   466  		// not yet have an address. This is okay because we would either schedule a
   467  		// report to be sent later or we will be notified when an address is added,
   468  		// at which point we will try to send messages again.
   469  		for groupAddress, info := range g.memberships {
   470  			if !g.shouldPerformForGroup(groupAddress) {
   471  				continue
   472  			}
   473  
   474  			// Revert the transmissions count since we did not successfully send.
   475  			info.transmissionLeft++
   476  			g.memberships[groupAddress] = info
   477  		}
   478  	}
   479  }
   480  
   481  // SendQueuedReportsLocked attempts to send reports for groups that failed to
   482  // send reports during their last attempt.
   483  //
   484  // Precondition: g.protocolMU must be locked.
   485  func (g *GenericMulticastProtocolState) SendQueuedReportsLocked() {
   486  	if g.stateChangedReportV2TimerSet {
   487  		return
   488  	}
   489  
   490  	for groupAddress, info := range g.memberships {
   491  		if info.delayedReportJobFiresAt.IsZero() {
   492  			switch g.mode {
   493  			case protocolModeV2:
   494  				g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode)
   495  			case protocolModeV1Compatibility, protocolModeV1:
   496  				g.maybeSendReportLocked(groupAddress, &info)
   497  			default:
   498  				panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   499  			}
   500  
   501  			g.memberships[groupAddress] = info
   502  		}
   503  	}
   504  }
   505  
   506  // JoinGroupLocked handles joining a new group.
   507  //
   508  // Precondition: g.protocolMU must be locked.
   509  func (g *GenericMulticastProtocolState) JoinGroupLocked(groupAddress tcpip.Address) {
   510  	info, ok := g.memberships[groupAddress]
   511  	if ok {
   512  		info.joins++
   513  		if info.joins > 1 {
   514  			// The group has already been joined.
   515  			g.memberships[groupAddress] = info
   516  			return
   517  		}
   518  	} else {
   519  		info = multicastGroupState{
   520  			// Since we just joined the group, its count is 1.
   521  			joins:            1,
   522  			lastToSendReport: false,
   523  			delayedReportJob: tcpip.NewJob(g.opts.Clock, g.protocolMU, func() {
   524  				if !g.opts.Protocol.Enabled() {
   525  					panic(fmt.Sprintf("delayed report job fired for group %s while the multicast group protocol is disabled", groupAddress))
   526  				}
   527  
   528  				info, ok := g.memberships[groupAddress]
   529  				if !ok {
   530  					panic(fmt.Sprintf("expected to find group state for group = %s", groupAddress))
   531  				}
   532  
   533  				info.delayedReportJobFiresAt = time.Time{}
   534  
   535  				switch g.mode {
   536  				case protocolModeV2:
   537  					reportBuilder := g.opts.Protocol.NewReportV2Builder()
   538  					reportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordModeIsExclude, groupAddress)
   539  					// Nothing meaningful we can do with the error here - we only try to
   540  					// send a delayed report once.
   541  					_, _ = reportBuilder.Send()
   542  				case protocolModeV1Compatibility, protocolModeV1:
   543  					g.maybeSendReportLocked(groupAddress, &info)
   544  				default:
   545  					panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   546  				}
   547  
   548  				info.clearQueriedIncludeSources()
   549  				g.memberships[groupAddress] = info
   550  			}),
   551  			queriedIncludeSources: make(map[tcpip.Address]struct{}),
   552  		}
   553  	}
   554  
   555  	info.deleteScheduled = false
   556  	info.clearQueriedIncludeSources()
   557  	info.delayedReportJobFiresAt = time.Time{}
   558  	info.lastToSendReport = false
   559  	g.initializeNewMemberLocked(groupAddress, &info, nil /* callersV2ReportBuilder */)
   560  	g.memberships[groupAddress] = info
   561  }
   562  
   563  // IsLocallyJoinedRLocked returns true if the group is locally joined.
   564  //
   565  // Precondition: g.protocolMU must be read locked.
   566  func (g *GenericMulticastProtocolState) IsLocallyJoinedRLocked(groupAddress tcpip.Address) bool {
   567  	info, ok := g.memberships[groupAddress]
   568  	return ok && !info.deleteScheduled
   569  }
   570  
   571  func (g *GenericMulticastProtocolState) sendV2ReportAndMaybeScheduleChangedTimer(
   572  	groupAddress tcpip.Address,
   573  	info *multicastGroupState,
   574  	recordType MulticastGroupProtocolV2ReportRecordType,
   575  ) bool {
   576  	if info.transmissionLeft == 0 {
   577  		return false
   578  	}
   579  
   580  	successfullySentAndHasMore := false
   581  
   582  	// Send a report immediately to announce us leaving the group.
   583  	reportBuilder := g.opts.Protocol.NewReportV2Builder()
   584  	reportBuilder.AddRecord(recordType, groupAddress)
   585  	if sent, err := reportBuilder.Send(); sent && err == nil {
   586  		info.transmissionLeft--
   587  
   588  		successfullySentAndHasMore = info.transmissionLeft != 0
   589  
   590  		// Use the interface-wide state changed report for further transmissions.
   591  		if successfullySentAndHasMore {
   592  			g.scheduleStateChangedTimer()
   593  		}
   594  	}
   595  
   596  	return successfullySentAndHasMore
   597  }
   598  
   599  func (g *GenericMulticastProtocolState) scheduleStateChangedTimer() {
   600  	if g.stateChangedReportV2TimerSet {
   601  		return
   602  	}
   603  
   604  	delay := g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay)
   605  	if g.stateChangedReportV2Timer == nil {
   606  		// TODO(https://issuetracker.google.com/264799098): Create timer on
   607  		// initialization instead of lazily creating the timer since the timer
   608  		// does not change after being created.
   609  		g.stateChangedReportV2Timer = g.opts.Clock.AfterFunc(delay, func() {
   610  			g.protocolMU.Lock()
   611  			defer g.protocolMU.Unlock()
   612  
   613  			reportBuilder := g.opts.Protocol.NewReportV2Builder()
   614  			nonEmptyReport := false
   615  			for groupAddress, info := range g.memberships {
   616  				if info.transmissionLeft == 0 || !g.shouldPerformForGroup(groupAddress) {
   617  					continue
   618  				}
   619  
   620  				info.transmissionLeft--
   621  				nonEmptyReport = true
   622  
   623  				mode := MulticastGroupProtocolV2ReportRecordChangeToExcludeMode
   624  				if info.deleteScheduled {
   625  					mode = MulticastGroupProtocolV2ReportRecordChangeToIncludeMode
   626  				}
   627  				reportBuilder.AddRecord(mode, groupAddress)
   628  
   629  				if info.deleteScheduled && info.transmissionLeft == 0 {
   630  					// No more transmissions left so we can actually delete the
   631  					// membership.
   632  					delete(g.memberships, groupAddress)
   633  				} else {
   634  					g.memberships[groupAddress] = info
   635  				}
   636  			}
   637  
   638  			// Nothing meaningful we can do with the error here. We will retry
   639  			// sending a state changed report again anyways.
   640  			_, _ = reportBuilder.Send()
   641  
   642  			if nonEmptyReport {
   643  				g.stateChangedReportV2Timer.Reset(g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay))
   644  			} else {
   645  				g.stateChangedReportV2TimerSet = false
   646  			}
   647  		})
   648  	} else {
   649  		g.stateChangedReportV2Timer.Reset(delay)
   650  	}
   651  	g.stateChangedReportV2TimerSet = true
   652  }
   653  
   654  // LeaveGroupLocked handles leaving the group.
   655  //
   656  // Returns false if the group is not currently joined.
   657  //
   658  // Precondition: g.protocolMU must be locked.
   659  func (g *GenericMulticastProtocolState) LeaveGroupLocked(groupAddress tcpip.Address) bool {
   660  	info, ok := g.memberships[groupAddress]
   661  	if !ok || info.joins == 0 {
   662  		return false
   663  	}
   664  
   665  	info.joins--
   666  	if info.joins != 0 {
   667  		// If we still have outstanding joins, then do nothing further.
   668  		g.memberships[groupAddress] = info
   669  		return true
   670  	}
   671  
   672  	info.deleteScheduled = true
   673  	info.cancelDelayedReportJob()
   674  
   675  	if !g.shouldPerformForGroup(groupAddress) {
   676  		delete(g.memberships, groupAddress)
   677  		return true
   678  	}
   679  
   680  	switch g.mode {
   681  	case protocolModeV2:
   682  		info.transmissionLeft = g.robustnessVariable
   683  		if g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, &info, MulticastGroupProtocolV2ReportRecordChangeToIncludeMode) {
   684  			g.memberships[groupAddress] = info
   685  		} else {
   686  			delete(g.memberships, groupAddress)
   687  		}
   688  	case protocolModeV1Compatibility, protocolModeV1:
   689  		g.transitionToNonMemberLocked(groupAddress, &info)
   690  		delete(g.memberships, groupAddress)
   691  	default:
   692  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   693  	}
   694  
   695  	return true
   696  }
   697  
   698  // HandleQueryV2Locked handles a V2 query.
   699  //
   700  // Precondition: g.protocolMU must be locked.
   701  func (g *GenericMulticastProtocolState) HandleQueryV2Locked(groupAddress tcpip.Address, maxResponseCode uint16, sources header.AddressIterator, robustnessVariable uint8, queryInterval time.Duration) {
   702  	if !g.opts.Protocol.Enabled() {
   703  		return
   704  	}
   705  
   706  	switch g.mode {
   707  	case protocolModeV1Compatibility, protocolModeV1:
   708  		g.handleQueryInnerLocked(groupAddress, g.opts.Protocol.V2QueryMaxRespCodeToV1Delay(maxResponseCode))
   709  		return
   710  	case protocolModeV2:
   711  	default:
   712  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   713  	}
   714  
   715  	if robustnessVariable != 0 {
   716  		g.robustnessVariable = robustnessVariable
   717  	}
   718  
   719  	if queryInterval != 0 {
   720  		g.queryInterval = queryInterval
   721  	}
   722  
   723  	maxResponseTime := g.calculateDelayTimerDuration(g.opts.Protocol.V2QueryMaxRespCodeToV2Delay(maxResponseCode))
   724  
   725  	// As per RFC 3376 section 5.2,
   726  	//
   727  	//   1. If there is a pending response to a previous General Query
   728  	//      scheduled sooner than the selected delay, no additional response
   729  	//      needs to be scheduled.
   730  	//
   731  	//   2. If the received Query is a General Query, the interface timer is
   732  	//      used to schedule a response to the General Query after the
   733  	//      selected delay.  Any previously pending response to a General
   734  	//      Query is canceled.
   735  	//
   736  	//   3. If the received Query is a Group-Specific Query or a Group-and-
   737  	//      Source-Specific Query and there is no pending response to a
   738  	//      previous Query for this group, then the group timer is used to
   739  	//      schedule a report.  If the received Query is a Group-and-Source-
   740  	//      Specific Query, the list of queried sources is recorded to be used
   741  	//      when generating a response.
   742  	//
   743  	//   4. If there already is a pending response to a previous Query
   744  	//      scheduled for this group, and either the new Query is a Group-
   745  	//      Specific Query or the recorded source-list associated with the
   746  	//      group is empty, then the group source-list is cleared and a single
   747  	//      response is scheduled using the group timer.  The new response is
   748  	//      scheduled to be sent at the earliest of the remaining time for the
   749  	//      pending report and the selected delay.
   750  	//
   751  	//   5. If the received Query is a Group-and-Source-Specific Query and
   752  	//      there is a pending response for this group with a non-empty
   753  	//      source-list, then the group source list is augmented to contain
   754  	//      the list of sources in the new Query and a single response is
   755  	//      scheduled using the group timer.  The new response is scheduled to
   756  	//      be sent at the earliest of the remaining time for the pending
   757  	//      report and the selected delay.
   758  	//
   759  	// As per RFC 3810 section 6.2,
   760  	//
   761  	//   1. If there is a pending response to a previous General Query
   762  	//      scheduled sooner than the selected delay, no additional response
   763  	//      needs to be scheduled.
   764  	//
   765  	//   2. If the received Query is a General Query, the Interface Timer is
   766  	//      used to schedule a response to the General Query after the
   767  	//      selected delay.  Any previously pending response to a General
   768  	//      Query is canceled.
   769  	//
   770  	//   3. If the received Query is a Multicast Address Specific Query or a
   771  	//      Multicast Address and Source Specific Query and there is no
   772  	//      pending response to a previous Query for this multicast address,
   773  	//      then the Multicast Address Timer is used to schedule a report.  If
   774  	//      the received Query is a Multicast Address and Source Specific
   775  	//      Query, the list of queried sources is recorded to be used when
   776  	//      generating a response.
   777  	//
   778  	//   4. If there is already a pending response to a previous Query
   779  	//      scheduled for this multicast address, and either the new Query is
   780  	//      a Multicast Address Specific Query or the recorded source list
   781  	//      associated with the multicast address is empty, then the multicast
   782  	//      address source list is cleared and a single response is scheduled,
   783  	//      using the Multicast Address Timer.  The new response is scheduled
   784  	//      to be sent at the earliest of the remaining time for the pending
   785  	//      report and the selected delay.
   786  	//
   787  	//   5. If the received Query is a Multicast Address and Source Specific
   788  	//      Query and there is a pending response for this multicast address
   789  	//      with a non-empty source list, then the multicast address source
   790  	//      list is augmented to contain the list of sources in the new Query,
   791  	//      and a single response is scheduled using the Multicast Address
   792  	//      Timer.  The new response is scheduled to be sent at the earliest
   793  	//      of the remaining time for the pending report and the selected
   794  	//      delay.
   795  	now := g.opts.Clock.Now()
   796  	if !g.generalQueryV2TimerFiresAt.IsZero() && g.generalQueryV2TimerFiresAt.Sub(now) <= maxResponseTime {
   797  		return
   798  	}
   799  
   800  	if groupAddress.Unspecified() {
   801  		if g.generalQueryV2Timer == nil {
   802  			// TODO(https://issuetracker.google.com/264799098): Create timer on
   803  			// initialization instead of lazily creating the timer since the timer
   804  			// does not change after being created.
   805  			g.generalQueryV2Timer = g.opts.Clock.AfterFunc(maxResponseTime, func() {
   806  				g.protocolMU.Lock()
   807  				defer g.protocolMU.Unlock()
   808  
   809  				g.generalQueryV2TimerFiresAt = time.Time{}
   810  
   811  				// As per RFC 3810 section 6.3,
   812  				//
   813  				//      If the expired timer is the Interface Timer (i.e., there is a
   814  				//      pending response to a General Query), then one Current State
   815  				//      Record is sent for each multicast address for which the specified
   816  				//      interface has listening state, as described in section 4.2.  The
   817  				//      Current State Record carries the multicast address and its
   818  				//      associated filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and
   819  				//      Source list.  Multiple Current State Records are packed into
   820  				//      individual Report messages, to the extent possible.
   821  				//
   822  				// As per RFC 3376 section 5.2,
   823  				//
   824  				//      If the expired timer is the interface timer (i.e., it is a pending
   825  				//      response to a General Query), then one Current-State Record is
   826  				//      sent for each multicast address for which the specified interface
   827  				//      has reception state, as described in section 3.2.  The Current-
   828  				//      State Record carries the multicast address and its associated
   829  				//      filter mode (MODE_IS_INCLUDE or MODE_IS_EXCLUDE) and source list.
   830  				//      Multiple Current-State Records are packed into individual Report
   831  				//      messages, to the extent possible.
   832  				reportBuilder := g.opts.Protocol.NewReportV2Builder()
   833  				for groupAddress, info := range g.memberships {
   834  					if info.deleteScheduled || !g.shouldPerformForGroup(groupAddress) {
   835  						continue
   836  					}
   837  
   838  					// A MODE_IS_EXCLUDE record without any sources indicates that we are
   839  					// interested in traffic from all sources for the group.
   840  					//
   841  					// We currently only hold groups if we have an active interest in the
   842  					// group.
   843  					reportBuilder.AddRecord(
   844  						MulticastGroupProtocolV2ReportRecordModeIsExclude,
   845  						groupAddress,
   846  					)
   847  				}
   848  
   849  				_, _ = reportBuilder.Send()
   850  			})
   851  		} else {
   852  			g.generalQueryV2Timer.Reset(maxResponseTime)
   853  		}
   854  		g.generalQueryV2TimerFiresAt = now.Add(maxResponseTime)
   855  		return
   856  	}
   857  
   858  	if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled && g.shouldPerformForGroup(groupAddress) {
   859  		if info.delayedReportJobFiresAt.IsZero() || (!sources.Done() && len(info.queriedIncludeSources) != 0) {
   860  			for {
   861  				source, ok := sources.Next()
   862  				if !ok {
   863  					break
   864  				}
   865  
   866  				info.queriedIncludeSources[source] = struct{}{}
   867  			}
   868  		} else {
   869  			info.clearQueriedIncludeSources()
   870  		}
   871  		g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime)
   872  		g.memberships[groupAddress] = info
   873  	}
   874  }
   875  
   876  // HandleQueryLocked handles a query message with the specified maximum response
   877  // time.
   878  //
   879  // If the group address is unspecified, then reports will be scheduled for all
   880  // joined groups.
   881  //
   882  // Report(s) will be scheduled to be sent after a random duration between 0 and
   883  // the maximum response time.
   884  //
   885  // Precondition: g.protocolMU must be locked.
   886  func (g *GenericMulticastProtocolState) HandleQueryLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) {
   887  	if !g.opts.Protocol.Enabled() {
   888  		return
   889  	}
   890  
   891  	switch g.mode {
   892  	case protocolModeV2, protocolModeV1Compatibility:
   893  		// As per 3376 section 8.12 (for IGMPv3),
   894  		//
   895  		//   The Older Version Querier Interval is the time-out for transitioning
   896  		//   a host back to IGMPv3 mode once an older version query is heard.
   897  		//   When an older version query is received, hosts set their Older
   898  		//   Version Querier Present Timer to Older Version Querier Interval.
   899  		//
   900  		//   This value MUST be ((the Robustness Variable) times (the Query
   901  		//   Interval in the last Query received)) plus (one Query Response
   902  		//   Interval).
   903  		//
   904  		// As per RFC 3810 section 9.12 (for MLDv2),
   905  		//
   906  		//   The Older Version Querier Present Timeout is the time-out for
   907  		//   transitioning a host back to MLDv2 Host Compatibility Mode.  When an
   908  		//   MLDv1 query is received, MLDv2 hosts set their Older Version Querier
   909  		//   Present Timer to [Older Version Querier Present Timeout].
   910  		//
   911  		//   This value MUST be ([Robustness Variable] times (the [Query Interval]
   912  		//   in the last Query received)) plus ([Query Response Interval]).
   913  		modeRevertDelay := time.Duration(g.robustnessVariable) * g.queryInterval
   914  		if g.modeTimer == nil {
   915  			// TODO(https://issuetracker.google.com/264799098): Create timer on
   916  			// initialization instead of lazily creating the timer since the timer
   917  			// does not change after being created.
   918  			g.modeTimer = g.opts.Clock.AfterFunc(modeRevertDelay, func() {
   919  				g.protocolMU.Lock()
   920  				defer g.protocolMU.Unlock()
   921  				g.mode = protocolModeV2
   922  			})
   923  		} else {
   924  			g.modeTimer.Reset(modeRevertDelay)
   925  		}
   926  		g.mode = protocolModeV1Compatibility
   927  		g.cancelV2ReportTimers()
   928  	case protocolModeV1:
   929  	default:
   930  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
   931  	}
   932  	g.handleQueryInnerLocked(groupAddress, maxResponseTime)
   933  }
   934  
   935  func (g *GenericMulticastProtocolState) handleQueryInnerLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) {
   936  	maxResponseTime = g.calculateDelayTimerDuration(maxResponseTime)
   937  
   938  	// As per RFC 2236 section 2.4 (for IGMPv2),
   939  	//
   940  	//   In a Membership Query message, the group address field is set to zero
   941  	//   when sending a General Query, and set to the group address being
   942  	//   queried when sending a Group-Specific Query.
   943  	//
   944  	// As per RFC 2710 section 3.6 (for MLDv1),
   945  	//
   946  	//   In a Query message, the Multicast Address field is set to zero when
   947  	//   sending a General Query, and set to a specific IPv6 multicast address
   948  	//   when sending a Multicast-Address-Specific Query.
   949  	if groupAddress.Unspecified() {
   950  		// This is a general query as the group address is unspecified.
   951  		for groupAddress, info := range g.memberships {
   952  			g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime)
   953  			g.memberships[groupAddress] = info
   954  		}
   955  	} else if info, ok := g.memberships[groupAddress]; ok && !info.deleteScheduled {
   956  		g.setDelayTimerForAddressLocked(groupAddress, &info, maxResponseTime)
   957  		g.memberships[groupAddress] = info
   958  	}
   959  }
   960  
   961  // HandleReportLocked handles a report message.
   962  //
   963  // If the report is for a joined group, any active delayed report will be
   964  // cancelled and the host state for the group transitions to idle.
   965  //
   966  // Precondition: g.protocolMU must be locked.
   967  func (g *GenericMulticastProtocolState) HandleReportLocked(groupAddress tcpip.Address) {
   968  	if !g.opts.Protocol.Enabled() {
   969  		return
   970  	}
   971  
   972  	// As per RFC 2236 section 3 pages 3-4 (for IGMPv2),
   973  	//
   974  	//   If the host receives another host's Report (version 1 or 2) while it has
   975  	//   a timer running, it stops its timer for the specified group and does not
   976  	//   send a Report
   977  	//
   978  	// As per RFC 2710 section 4 page 6 (for MLDv1),
   979  	//
   980  	//   If a node receives another node's Report from an interface for a
   981  	//   multicast address while it has a timer running for that same address
   982  	//   on that interface, it stops its timer and does not send a Report for
   983  	//   that address, thus suppressing duplicate reports on the link.
   984  	if info, ok := g.memberships[groupAddress]; ok {
   985  		info.cancelDelayedReportJob()
   986  		info.lastToSendReport = false
   987  		g.memberships[groupAddress] = info
   988  	}
   989  }
   990  
   991  // initializeNewMemberLocked initializes a new group membership.
   992  //
   993  // Precondition: g.protocolMU must be locked.
   994  func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress tcpip.Address, info *multicastGroupState, callersV2ReportBuilder MulticastGroupProtocolV2ReportBuilder) {
   995  	if !g.shouldPerformForGroup(groupAddress) {
   996  		return
   997  	}
   998  
   999  	info.lastToSendReport = false
  1000  
  1001  	switch g.mode {
  1002  	case protocolModeV2:
  1003  		info.transmissionLeft = g.robustnessVariable
  1004  		if callersV2ReportBuilder == nil {
  1005  			g.sendV2ReportAndMaybeScheduleChangedTimer(groupAddress, info, MulticastGroupProtocolV2ReportRecordChangeToExcludeMode)
  1006  		} else {
  1007  			callersV2ReportBuilder.AddRecord(MulticastGroupProtocolV2ReportRecordChangeToExcludeMode, groupAddress)
  1008  			info.transmissionLeft--
  1009  		}
  1010  	case protocolModeV1Compatibility, protocolModeV1:
  1011  		info.transmissionLeft = unsolicitedTransmissionCount
  1012  		g.maybeSendReportLocked(groupAddress, info)
  1013  	default:
  1014  		panic(fmt.Sprintf("unrecognized mode = %d", g.mode))
  1015  	}
  1016  }
  1017  
  1018  func (g *GenericMulticastProtocolState) shouldPerformForGroup(groupAddress tcpip.Address) bool {
  1019  	return g.opts.Protocol.ShouldPerformProtocol(groupAddress) && g.opts.Protocol.Enabled()
  1020  }
  1021  
  1022  // maybeSendReportLocked attempts to send a report for a group.
  1023  //
  1024  // Precondition: g.protocolMU must be locked.
  1025  func (g *GenericMulticastProtocolState) maybeSendReportLocked(groupAddress tcpip.Address, info *multicastGroupState) {
  1026  	if info.transmissionLeft == 0 {
  1027  		return
  1028  	}
  1029  
  1030  	// As per RFC 2236 section 3 page 5 (for IGMPv2),
  1031  	//
  1032  	//   When a host joins a multicast group, it should immediately transmit an
  1033  	//   unsolicited Version 2 Membership Report for that group" ... "it is
  1034  	//   recommended that it be repeated".
  1035  	//
  1036  	// As per RFC 2710 section 4 page 6 (for MLDv1),
  1037  	//
  1038  	//   When a node starts listening to a multicast address on an interface,
  1039  	//   it should immediately transmit an unsolicited Report for that address
  1040  	//   on that interface, in case it is the first listener on the link. To
  1041  	//   cover the possibility of the initial Report being lost or damaged, it
  1042  	//   is recommended that it be repeated once or twice after short delays
  1043  	//   [Unsolicited Report Interval].
  1044  	//
  1045  	// TODO(gvisor.dev/issue/4901): Support a configurable number of initial
  1046  	// unsolicited reports.
  1047  	sent, err := g.opts.Protocol.SendReport(groupAddress)
  1048  	if err == nil && sent {
  1049  		info.lastToSendReport = true
  1050  
  1051  		info.transmissionLeft--
  1052  		if info.transmissionLeft > 0 {
  1053  			g.setDelayTimerForAddressLocked(
  1054  				groupAddress,
  1055  				info,
  1056  				g.calculateDelayTimerDuration(g.opts.MaxUnsolicitedReportDelay),
  1057  			)
  1058  		}
  1059  	}
  1060  }
  1061  
  1062  // maybeSendLeave attempts to send a leave message.
  1063  func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Address, lastToSendReport bool) {
  1064  	if !g.shouldPerformForGroup(groupAddress) || !lastToSendReport {
  1065  		return
  1066  	}
  1067  
  1068  	// Okay to ignore the error here as if packet write failed, the multicast
  1069  	// routers will eventually drop our membership anyways. If the interface is
  1070  	// being disabled or removed, the generic multicast protocol's should be
  1071  	// cleared eventually.
  1072  	//
  1073  	// As per RFC 2236 section 3 page 5 (for IGMPv2),
  1074  	//
  1075  	//   When a router receives a Report, it adds the group being reported to
  1076  	//   the list of multicast group memberships on the network on which it
  1077  	//   received the Report and sets the timer for the membership to the
  1078  	//   [Group Membership Interval]. Repeated Reports refresh the timer. If
  1079  	//   no Reports are received for a particular group before this timer has
  1080  	//   expired, the router assumes that the group has no local members and
  1081  	//   that it need not forward remotely-originated multicasts for that
  1082  	//   group onto the attached network.
  1083  	//
  1084  	// As per RFC 2710 section 4 page 5 (for MLDv1),
  1085  	//
  1086  	//   When a router receives a Report from a link, if the reported address
  1087  	//   is not already present in the router's list of multicast address
  1088  	//   having listeners on that link, the reported address is added to the
  1089  	//   list, its timer is set to [Multicast Listener Interval], and its
  1090  	//   appearance is made known to the router's multicast routing component.
  1091  	//   If a Report is received for a multicast address that is already
  1092  	//   present in the router's list, the timer for that address is reset to
  1093  	//   [Multicast Listener Interval]. If an address's timer expires, it is
  1094  	//   assumed that there are no longer any listeners for that address
  1095  	//   present on the link, so it is deleted from the list and its
  1096  	//   disappearance is made known to the multicast routing component.
  1097  	//
  1098  	// The requirement to send a leave message is also optional (it MAY be
  1099  	// skipped):
  1100  	//
  1101  	// As per RFC 2236 section 6 page 8 (for IGMPv2),
  1102  	//
  1103  	//  "send leave" for the group on the interface. If the interface
  1104  	//   state says the Querier is running IGMPv1, this action SHOULD be
  1105  	//   skipped. If the flag saying we were the last host to report is
  1106  	//   cleared, this action MAY be skipped. The Leave Message is sent to
  1107  	//   the ALL-ROUTERS group (224.0.0.2).
  1108  	//
  1109  	// As per RFC 2710 section 5 page 8 (for MLDv1),
  1110  	//
  1111  	//   "send done" for the address on the interface. If the flag saying
  1112  	//   we were the last node to report is cleared, this action MAY be
  1113  	//   skipped. The Done message is sent to the link-scope all-routers
  1114  	//   address (FF02::2).
  1115  	_ = g.opts.Protocol.SendLeave(groupAddress)
  1116  }
  1117  
  1118  // transitionToNonMemberLocked transitions the given multicast group the the
  1119  // non-member/listener state.
  1120  //
  1121  // Precondition: g.protocolMU must be locked.
  1122  func (g *GenericMulticastProtocolState) transitionToNonMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) {
  1123  	info.cancelDelayedReportJob()
  1124  	g.maybeSendLeave(groupAddress, info.lastToSendReport)
  1125  	info.lastToSendReport = false
  1126  }
  1127  
  1128  // setDelayTimerForAddressLocked sets timer to send a delayed report.
  1129  //
  1130  // Precondition: g.protocolMU MUST be locked.
  1131  func (g *GenericMulticastProtocolState) setDelayTimerForAddressLocked(groupAddress tcpip.Address, info *multicastGroupState, maxResponseTime time.Duration) {
  1132  	if !g.shouldPerformForGroup(groupAddress) {
  1133  		return
  1134  	}
  1135  
  1136  	if info.transmissionLeft < minQueryResponseTransmissionCount {
  1137  		info.transmissionLeft = minQueryResponseTransmissionCount
  1138  	}
  1139  
  1140  	// As per RFC 2236 section 3 page 3 (for IGMPv2),
  1141  	//
  1142  	//   If a timer for the group is already running, it is reset to the random
  1143  	//   value only if the requested Max Response Time is less than the remaining
  1144  	//   value of the running timer.
  1145  	//
  1146  	// As per RFC 2710 section 4 page 5 (for MLDv1),
  1147  	//
  1148  	//   If a timer for any address is already running, it is reset to the new
  1149  	//   random value only if the requested Maximum Response Delay is less than
  1150  	//   the remaining value of the running timer.
  1151  	now := g.opts.Clock.Now()
  1152  	if !info.delayedReportJobFiresAt.IsZero() && info.delayedReportJobFiresAt.Sub(now) <= maxResponseTime {
  1153  		// The timer is scheduled to fire before the maximum response time so we
  1154  		// leave our timer as is.
  1155  		return
  1156  	}
  1157  
  1158  	info.delayedReportJob.Cancel()
  1159  	info.delayedReportJob.Schedule(maxResponseTime)
  1160  	info.delayedReportJobFiresAt = now.Add(maxResponseTime)
  1161  }
  1162  
  1163  // calculateDelayTimerDuration returns a random time between (0, maxRespTime].
  1164  func (g *GenericMulticastProtocolState) calculateDelayTimerDuration(maxRespTime time.Duration) time.Duration {
  1165  	// As per RFC 2236 section 3 page 3 (for IGMPv2),
  1166  	//
  1167  	//   When a host receives a Group-Specific Query, it sets a delay timer to a
  1168  	//   random value selected from the range (0, Max Response Time]...
  1169  	//
  1170  	// As per RFC 2710 section 4 page 6 (for MLDv1),
  1171  	//
  1172  	//   When a node receives a Multicast-Address-Specific Query, if it is
  1173  	//   listening to the queried Multicast Address on the interface from
  1174  	//   which the Query was received, it sets a delay timer for that address
  1175  	//   to a random value selected from the range [0, Maximum Response Delay],
  1176  	//   as above.
  1177  	if maxRespTime == 0 {
  1178  		return 0
  1179  	}
  1180  	return time.Duration(g.opts.Rand.Int63n(int64(maxRespTime)))
  1181  }