github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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/SagerNet/gvisor/pkg/sync"
    23  	"github.com/SagerNet/gvisor/pkg/tcpip"
    24  )
    25  
    26  // hostState is the state a host may be in for a multicast group.
    27  type hostState int
    28  
    29  // The states below are generic across IGMPv2 (RFC 2236 section 6) and MLDv1
    30  // (RFC 2710 section 5). Even though the states are generic across both IGMPv2
    31  // and MLDv1, IGMPv2 terminology will be used.
    32  //
    33  //                                  ______________receive query______________
    34  //                                 |                                         |
    35  //                                 |   _____send or receive report_____      |
    36  //                                 |  |                                |     |
    37  //                                 V  |                                V     |
    38  //  +-------+ +-----------+ +------------+ +-------------------+ +--------+  |
    39  //  | Non-M | | Pending-M | | Delaying-M | | Queued Delaying-M | | Idle-M | -
    40  //  +-------+ +-----------+ +------------+ +-------------------+ +--------+
    41  //    |          ^      |       ^      |          ^       |             ^
    42  //    |          |      |       |      |          |       |             |
    43  //     ----------        -------        ----------         -------------
    44  //   initialize new    send inital     fail to send       send or receive
    45  //  group membership     report       delayed report          report
    46  //
    47  // Not shown in the diagram above, but any state may transition into the non
    48  // member state when a group is left.
    49  const (
    50  	// nonMember is the "'Non-Member' state, when the host does not belong to the
    51  	// group on the interface. This is the initial state for all memberships on
    52  	// all network interfaces; it requires no storage in the host."
    53  	//
    54  	// 'Non-Listener' is the MLDv1 term used to describe this state.
    55  	//
    56  	// This state is used to keep track of groups that have been joined locally,
    57  	// but without advertising the membership to the network.
    58  	nonMember hostState = iota
    59  
    60  	// pendingMember is a newly joined member that is waiting to successfully send
    61  	// the initial set of reports.
    62  	//
    63  	// This is not an RFC defined state; it is an implementation specific state to
    64  	// track that the initial report needs to be sent.
    65  	//
    66  	// MAY NOT transition to the idle member state from this state.
    67  	pendingMember
    68  
    69  	// delayingMember is the "'Delaying Member' state, when the host belongs to
    70  	// the group on the interface and has a report delay timer running for that
    71  	// membership."
    72  	//
    73  	// 'Delaying Listener' is the MLDv1 term used to describe this state.
    74  	delayingMember
    75  
    76  	// queuedDelayingMember is a delayingMember that failed to send a report after
    77  	// its delayed report timer fired. Hosts in this state are waiting to attempt
    78  	// retransmission of the delayed report.
    79  	//
    80  	// This is not an RFC defined state; it is an implementation specific state to
    81  	// track that the delayed report needs to be sent.
    82  	//
    83  	// May transition to idle member if a report is received for a group.
    84  	queuedDelayingMember
    85  
    86  	// idleMember is the "Idle Member" state, when the host belongs to the group
    87  	// on the interface and does not have a report delay timer running for that
    88  	// membership.
    89  	//
    90  	// 'Idle Listener' is the MLDv1 term used to describe this state.
    91  	idleMember
    92  )
    93  
    94  func (s hostState) isDelayingMember() bool {
    95  	switch s {
    96  	case nonMember, pendingMember, idleMember:
    97  		return false
    98  	case delayingMember, queuedDelayingMember:
    99  		return true
   100  	default:
   101  		panic(fmt.Sprintf("unrecognized host state = %d", s))
   102  	}
   103  }
   104  
   105  // multicastGroupState holds the Generic Multicast Protocol state for a
   106  // multicast group.
   107  type multicastGroupState struct {
   108  	// joins is the number of times the group has been joined.
   109  	joins uint64
   110  
   111  	// state holds the host's state for the group.
   112  	state hostState
   113  
   114  	// lastToSendReport is true if we sent the last report for the group. It is
   115  	// used to track whether there are other hosts on the subnet that are also
   116  	// members of the group.
   117  	//
   118  	// Defined in RFC 2236 section 6 page 9 for IGMPv2 and RFC 2710 section 5 page
   119  	// 8 for MLDv1.
   120  	lastToSendReport bool
   121  
   122  	// delayedReportJob is used to delay sending responses to membership report
   123  	// messages in order to reduce duplicate reports from multiple hosts on the
   124  	// interface.
   125  	//
   126  	// Must not be nil.
   127  	delayedReportJob *tcpip.Job
   128  
   129  	// delyedReportJobFiresAt is the time when the delayed report job will fire.
   130  	//
   131  	// A zero value indicates that the job is not scheduled.
   132  	delayedReportJobFiresAt time.Time
   133  }
   134  
   135  func (m *multicastGroupState) cancelDelayedReportJob() {
   136  	m.delayedReportJob.Cancel()
   137  	m.delayedReportJobFiresAt = time.Time{}
   138  }
   139  
   140  // GenericMulticastProtocolOptions holds options for the generic multicast
   141  // protocol.
   142  type GenericMulticastProtocolOptions struct {
   143  	// Rand is the source of random numbers.
   144  	Rand *rand.Rand
   145  
   146  	// Clock is the clock used to create timers.
   147  	Clock tcpip.Clock
   148  
   149  	// Protocol is the implementation of the variant of multicast group protocol
   150  	// in use.
   151  	Protocol MulticastGroupProtocol
   152  
   153  	// MaxUnsolicitedReportDelay is the maximum amount of time to wait between
   154  	// transmitting unsolicited reports.
   155  	//
   156  	// Unsolicited reports are transmitted when a group is newly joined.
   157  	MaxUnsolicitedReportDelay time.Duration
   158  }
   159  
   160  // MulticastGroupProtocol is a multicast group protocol whose core state machine
   161  // can be represented by GenericMulticastProtocolState.
   162  type MulticastGroupProtocol interface {
   163  	// Enabled indicates whether the generic multicast protocol will be
   164  	// performed.
   165  	//
   166  	// When enabled, the protocol may transmit report and leave messages when
   167  	// joining and leaving multicast groups respectively, and handle incoming
   168  	// packets.
   169  	//
   170  	// When disabled, the protocol will still keep track of locally joined groups,
   171  	// it just won't transmit and handle packets, or update groups' state.
   172  	Enabled() bool
   173  
   174  	// SendReport sends a multicast report for the specified group address.
   175  	//
   176  	// Returns false if the caller should queue the report to be sent later. Note,
   177  	// returning false does not mean that the receiver hit an error.
   178  	SendReport(groupAddress tcpip.Address) (sent bool, err tcpip.Error)
   179  
   180  	// SendLeave sends a multicast leave for the specified group address.
   181  	SendLeave(groupAddress tcpip.Address) tcpip.Error
   182  
   183  	// ShouldPerformProtocol returns true iff the protocol should be performed for
   184  	// the specified group.
   185  	ShouldPerformProtocol(tcpip.Address) bool
   186  }
   187  
   188  // GenericMulticastProtocolState is the per interface generic multicast protocol
   189  // state.
   190  //
   191  // There is actually no protocol named "Generic Multicast Protocol". Instead,
   192  // the term used to refer to a generic multicast protocol that applies to both
   193  // IPv4 and IPv6. Specifically, Generic Multicast Protocol is the core state
   194  // machine of IGMPv2 as defined by RFC 2236 and MLDv1 as defined by RFC 2710.
   195  //
   196  // Callers must synchronize accesses to the generic multicast protocol state;
   197  // GenericMulticastProtocolState obtains no locks in any of its methods. The
   198  // only exception to this is GenericMulticastProtocolState's timer/job callbacks
   199  // which will obtain the lock provided to the GenericMulticastProtocolState when
   200  // it is initialized.
   201  //
   202  // GenericMulticastProtocolState.Init MUST be called before calling any of
   203  // the methods on GenericMulticastProtocolState.
   204  //
   205  // GenericMulticastProtocolState.MakeAllNonMemberLocked MUST be called when the
   206  // multicast group protocol is disabled so that leave messages may be sent.
   207  type GenericMulticastProtocolState struct {
   208  	// Do not allow overwriting this state.
   209  	_ sync.NoCopy
   210  
   211  	opts GenericMulticastProtocolOptions
   212  
   213  	// memberships holds group addresses and their associated state.
   214  	memberships map[tcpip.Address]multicastGroupState
   215  
   216  	// protocolMU is the mutex used to protect the protocol.
   217  	protocolMU *sync.RWMutex
   218  }
   219  
   220  // Init initializes the Generic Multicast Protocol state.
   221  //
   222  // Must only be called once for the lifetime of g; Init will panic if it is
   223  // called twice.
   224  //
   225  // The GenericMulticastProtocolState will only grab the lock when timers/jobs
   226  // fire.
   227  //
   228  // Note: the methods on opts.Protocol will always be called while protocolMU is
   229  // held.
   230  func (g *GenericMulticastProtocolState) Init(protocolMU *sync.RWMutex, opts GenericMulticastProtocolOptions) {
   231  	if g.memberships != nil {
   232  		panic("attempted to initialize generic membership protocol state twice")
   233  	}
   234  
   235  	*g = GenericMulticastProtocolState{
   236  		opts:        opts,
   237  		memberships: make(map[tcpip.Address]multicastGroupState),
   238  		protocolMU:  protocolMU,
   239  	}
   240  }
   241  
   242  // MakeAllNonMemberLocked transitions all groups to the non-member state.
   243  //
   244  // The groups will still be considered joined locally.
   245  //
   246  // MUST be called when the multicast group protocol is disabled.
   247  //
   248  // Precondition: g.protocolMU must be locked.
   249  func (g *GenericMulticastProtocolState) MakeAllNonMemberLocked() {
   250  	if !g.opts.Protocol.Enabled() {
   251  		return
   252  	}
   253  
   254  	for groupAddress, info := range g.memberships {
   255  		g.transitionToNonMemberLocked(groupAddress, &info)
   256  		g.memberships[groupAddress] = info
   257  	}
   258  }
   259  
   260  // InitializeGroupsLocked initializes each group, as if they were newly joined
   261  // but without affecting the groups' join count.
   262  //
   263  // Must only be called after calling MakeAllNonMember as a group should not be
   264  // initialized while it is not in the non-member state.
   265  //
   266  // Precondition: g.protocolMU must be locked.
   267  func (g *GenericMulticastProtocolState) InitializeGroupsLocked() {
   268  	if !g.opts.Protocol.Enabled() {
   269  		return
   270  	}
   271  
   272  	for groupAddress, info := range g.memberships {
   273  		g.initializeNewMemberLocked(groupAddress, &info)
   274  		g.memberships[groupAddress] = info
   275  	}
   276  }
   277  
   278  // SendQueuedReportsLocked attempts to send reports for groups that failed to
   279  // send reports during their last attempt.
   280  //
   281  // Precondition: g.protocolMU must be locked.
   282  func (g *GenericMulticastProtocolState) SendQueuedReportsLocked() {
   283  	for groupAddress, info := range g.memberships {
   284  		switch info.state {
   285  		case nonMember, delayingMember, idleMember:
   286  		case pendingMember:
   287  			// pendingMembers failed to send their initial unsolicited report so try
   288  			// to send the report and queue the extra unsolicited reports.
   289  			g.maybeSendInitialReportLocked(groupAddress, &info)
   290  		case queuedDelayingMember:
   291  			// queuedDelayingMembers failed to send their delayed reports so try to
   292  			// send the report and transition them to the idle state.
   293  			g.maybeSendDelayedReportLocked(groupAddress, &info)
   294  		default:
   295  			panic(fmt.Sprintf("unrecognized host state = %d", info.state))
   296  		}
   297  		g.memberships[groupAddress] = info
   298  	}
   299  }
   300  
   301  // JoinGroupLocked handles joining a new group.
   302  //
   303  // Precondition: g.protocolMU must be locked.
   304  func (g *GenericMulticastProtocolState) JoinGroupLocked(groupAddress tcpip.Address) {
   305  	if info, ok := g.memberships[groupAddress]; ok {
   306  		// The group has already been joined.
   307  		info.joins++
   308  		g.memberships[groupAddress] = info
   309  		return
   310  	}
   311  
   312  	info := multicastGroupState{
   313  		// Since we just joined the group, its count is 1.
   314  		joins: 1,
   315  		// The state will be updated below, if required.
   316  		state:            nonMember,
   317  		lastToSendReport: false,
   318  		delayedReportJob: tcpip.NewJob(g.opts.Clock, g.protocolMU, func() {
   319  			if !g.opts.Protocol.Enabled() {
   320  				panic(fmt.Sprintf("delayed report job fired for group %s while the multicast group protocol is disabled", groupAddress))
   321  			}
   322  
   323  			info, ok := g.memberships[groupAddress]
   324  			if !ok {
   325  				panic(fmt.Sprintf("expected to find group state for group = %s", groupAddress))
   326  			}
   327  
   328  			g.maybeSendDelayedReportLocked(groupAddress, &info)
   329  			g.memberships[groupAddress] = info
   330  		}),
   331  	}
   332  
   333  	if g.opts.Protocol.Enabled() {
   334  		g.initializeNewMemberLocked(groupAddress, &info)
   335  	}
   336  
   337  	g.memberships[groupAddress] = info
   338  }
   339  
   340  // IsLocallyJoinedRLocked returns true if the group is locally joined.
   341  //
   342  // Precondition: g.protocolMU must be read locked.
   343  func (g *GenericMulticastProtocolState) IsLocallyJoinedRLocked(groupAddress tcpip.Address) bool {
   344  	_, ok := g.memberships[groupAddress]
   345  	return ok
   346  }
   347  
   348  // LeaveGroupLocked handles leaving the group.
   349  //
   350  // Returns false if the group is not currently joined.
   351  //
   352  // Precondition: g.protocolMU must be locked.
   353  func (g *GenericMulticastProtocolState) LeaveGroupLocked(groupAddress tcpip.Address) bool {
   354  	info, ok := g.memberships[groupAddress]
   355  	if !ok {
   356  		return false
   357  	}
   358  
   359  	if info.joins == 0 {
   360  		panic(fmt.Sprintf("tried to leave group %s with a join count of 0", groupAddress))
   361  	}
   362  	info.joins--
   363  	if info.joins != 0 {
   364  		// If we still have outstanding joins, then do nothing further.
   365  		g.memberships[groupAddress] = info
   366  		return true
   367  	}
   368  
   369  	g.transitionToNonMemberLocked(groupAddress, &info)
   370  	delete(g.memberships, groupAddress)
   371  	return true
   372  }
   373  
   374  // HandleQueryLocked handles a query message with the specified maximum response
   375  // time.
   376  //
   377  // If the group address is unspecified, then reports will be scheduled for all
   378  // joined groups.
   379  //
   380  // Report(s) will be scheduled to be sent after a random duration between 0 and
   381  // the maximum response time.
   382  //
   383  // Precondition: g.protocolMU must be locked.
   384  func (g *GenericMulticastProtocolState) HandleQueryLocked(groupAddress tcpip.Address, maxResponseTime time.Duration) {
   385  	if !g.opts.Protocol.Enabled() {
   386  		return
   387  	}
   388  
   389  	// As per RFC 2236 section 2.4 (for IGMPv2),
   390  	//
   391  	//   In a Membership Query message, the group address field is set to zero
   392  	//   when sending a General Query, and set to the group address being
   393  	//   queried when sending a Group-Specific Query.
   394  	//
   395  	// As per RFC 2710 section 3.6 (for MLDv1),
   396  	//
   397  	//   In a Query message, the Multicast Address field is set to zero when
   398  	//   sending a General Query, and set to a specific IPv6 multicast address
   399  	//   when sending a Multicast-Address-Specific Query.
   400  	if groupAddress.Unspecified() {
   401  		// This is a general query as the group address is unspecified.
   402  		for groupAddress, info := range g.memberships {
   403  			g.setDelayTimerForAddressRLocked(groupAddress, &info, maxResponseTime)
   404  			g.memberships[groupAddress] = info
   405  		}
   406  	} else if info, ok := g.memberships[groupAddress]; ok {
   407  		g.setDelayTimerForAddressRLocked(groupAddress, &info, maxResponseTime)
   408  		g.memberships[groupAddress] = info
   409  	}
   410  }
   411  
   412  // HandleReportLocked handles a report message.
   413  //
   414  // If the report is for a joined group, any active delayed report will be
   415  // cancelled and the host state for the group transitions to idle.
   416  //
   417  // Precondition: g.protocolMU must be locked.
   418  func (g *GenericMulticastProtocolState) HandleReportLocked(groupAddress tcpip.Address) {
   419  	if !g.opts.Protocol.Enabled() {
   420  		return
   421  	}
   422  
   423  	// As per RFC 2236 section 3 pages 3-4 (for IGMPv2),
   424  	//
   425  	//   If the host receives another host's Report (version 1 or 2) while it has
   426  	//   a timer running, it stops its timer for the specified group and does not
   427  	//   send a Report
   428  	//
   429  	// As per RFC 2710 section 4 page 6 (for MLDv1),
   430  	//
   431  	//   If a node receives another node's Report from an interface for a
   432  	//   multicast address while it has a timer running for that same address
   433  	//   on that interface, it stops its timer and does not send a Report for
   434  	//   that address, thus suppressing duplicate reports on the link.
   435  	if info, ok := g.memberships[groupAddress]; ok && info.state.isDelayingMember() {
   436  		info.cancelDelayedReportJob()
   437  		info.lastToSendReport = false
   438  		info.state = idleMember
   439  		g.memberships[groupAddress] = info
   440  	}
   441  }
   442  
   443  // initializeNewMemberLocked initializes a new group membership.
   444  //
   445  // Precondition: g.protocolMU must be locked.
   446  func (g *GenericMulticastProtocolState) initializeNewMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) {
   447  	if info.state != nonMember {
   448  		panic(fmt.Sprintf("host must be in non-member state to be initialized; group = %s, state = %d", groupAddress, info.state))
   449  	}
   450  
   451  	info.lastToSendReport = false
   452  
   453  	if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
   454  		info.state = idleMember
   455  		return
   456  	}
   457  
   458  	info.state = pendingMember
   459  	g.maybeSendInitialReportLocked(groupAddress, info)
   460  }
   461  
   462  // maybeSendInitialReportLocked attempts to start transmission of the initial
   463  // set of reports after newly joining a group.
   464  //
   465  // Host must be in pending member state.
   466  //
   467  // Precondition: g.protocolMU must be locked.
   468  func (g *GenericMulticastProtocolState) maybeSendInitialReportLocked(groupAddress tcpip.Address, info *multicastGroupState) {
   469  	if info.state != pendingMember {
   470  		panic(fmt.Sprintf("host must be in pending member state to send initial reports; group = %s, state = %d", groupAddress, info.state))
   471  	}
   472  
   473  	// As per RFC 2236 section 3 page 5 (for IGMPv2),
   474  	//
   475  	//   When a host joins a multicast group, it should immediately transmit an
   476  	//   unsolicited Version 2 Membership Report for that group" ... "it is
   477  	//   recommended that it be repeated".
   478  	//
   479  	// As per RFC 2710 section 4 page 6 (for MLDv1),
   480  	//
   481  	//   When a node starts listening to a multicast address on an interface,
   482  	//   it should immediately transmit an unsolicited Report for that address
   483  	//   on that interface, in case it is the first listener on the link. To
   484  	//   cover the possibility of the initial Report being lost or damaged, it
   485  	//   is recommended that it be repeated once or twice after short delays
   486  	//   [Unsolicited Report Interval].
   487  	//
   488  	// TODO(github.com/SagerNet/issue/4901): Support a configurable number of initial
   489  	// unsolicited reports.
   490  	sent, err := g.opts.Protocol.SendReport(groupAddress)
   491  	if err == nil && sent {
   492  		info.lastToSendReport = true
   493  		g.setDelayTimerForAddressRLocked(groupAddress, info, g.opts.MaxUnsolicitedReportDelay)
   494  	}
   495  }
   496  
   497  // maybeSendDelayedReportLocked attempts to send the delayed report.
   498  //
   499  // Host must be in pending, delaying or queued delaying member state.
   500  //
   501  // Precondition: g.protocolMU must be locked.
   502  func (g *GenericMulticastProtocolState) maybeSendDelayedReportLocked(groupAddress tcpip.Address, info *multicastGroupState) {
   503  	if !info.state.isDelayingMember() {
   504  		panic(fmt.Sprintf("host must be in delaying or queued delaying member state to send delayed reports; group = %s, state = %d", groupAddress, info.state))
   505  	}
   506  
   507  	sent, err := g.opts.Protocol.SendReport(groupAddress)
   508  	if err == nil && sent {
   509  		info.lastToSendReport = true
   510  		info.state = idleMember
   511  	} else {
   512  		info.state = queuedDelayingMember
   513  	}
   514  }
   515  
   516  // maybeSendLeave attempts to send a leave message.
   517  func (g *GenericMulticastProtocolState) maybeSendLeave(groupAddress tcpip.Address, lastToSendReport bool) {
   518  	if !g.opts.Protocol.Enabled() || !lastToSendReport {
   519  		return
   520  	}
   521  
   522  	if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
   523  		return
   524  	}
   525  
   526  	// Okay to ignore the error here as if packet write failed, the multicast
   527  	// routers will eventually drop our membership anyways. If the interface is
   528  	// being disabled or removed, the generic multicast protocol's should be
   529  	// cleared eventually.
   530  	//
   531  	// As per RFC 2236 section 3 page 5 (for IGMPv2),
   532  	//
   533  	//   When a router receives a Report, it adds the group being reported to
   534  	//   the list of multicast group memberships on the network on which it
   535  	//   received the Report and sets the timer for the membership to the
   536  	//   [Group Membership Interval]. Repeated Reports refresh the timer. If
   537  	//   no Reports are received for a particular group before this timer has
   538  	//   expired, the router assumes that the group has no local members and
   539  	//   that it need not forward remotely-originated multicasts for that
   540  	//   group onto the attached network.
   541  	//
   542  	// As per RFC 2710 section 4 page 5 (for MLDv1),
   543  	//
   544  	//   When a router receives a Report from a link, if the reported address
   545  	//   is not already present in the router's list of multicast address
   546  	//   having listeners on that link, the reported address is added to the
   547  	//   list, its timer is set to [Multicast Listener Interval], and its
   548  	//   appearance is made known to the router's multicast routing component.
   549  	//   If a Report is received for a multicast address that is already
   550  	//   present in the router's list, the timer for that address is reset to
   551  	//   [Multicast Listener Interval]. If an address's timer expires, it is
   552  	//   assumed that there are no longer any listeners for that address
   553  	//   present on the link, so it is deleted from the list and its
   554  	//   disappearance is made known to the multicast routing component.
   555  	//
   556  	// The requirement to send a leave message is also optional (it MAY be
   557  	// skipped):
   558  	//
   559  	// As per RFC 2236 section 6 page 8 (for IGMPv2),
   560  	//
   561  	//  "send leave" for the group on the interface. If the interface
   562  	//   state says the Querier is running IGMPv1, this action SHOULD be
   563  	//   skipped. If the flag saying we were the last host to report is
   564  	//   cleared, this action MAY be skipped. The Leave Message is sent to
   565  	//   the ALL-ROUTERS group (224.0.0.2).
   566  	//
   567  	// As per RFC 2710 section 5 page 8 (for MLDv1),
   568  	//
   569  	//   "send done" for the address on the interface. If the flag saying
   570  	//   we were the last node to report is cleared, this action MAY be
   571  	//   skipped. The Done message is sent to the link-scope all-routers
   572  	//   address (FF02::2).
   573  	_ = g.opts.Protocol.SendLeave(groupAddress)
   574  }
   575  
   576  // transitionToNonMemberLocked transitions the given multicast group the the
   577  // non-member/listener state.
   578  //
   579  // Precondition: g.protocolMU must be locked.
   580  func (g *GenericMulticastProtocolState) transitionToNonMemberLocked(groupAddress tcpip.Address, info *multicastGroupState) {
   581  	if info.state == nonMember {
   582  		return
   583  	}
   584  
   585  	info.cancelDelayedReportJob()
   586  	g.maybeSendLeave(groupAddress, info.lastToSendReport)
   587  	info.lastToSendReport = false
   588  	info.state = nonMember
   589  }
   590  
   591  // setDelayTimerForAddressRLocked sets timer to send a delay report.
   592  //
   593  // Precondition: g.protocolMU MUST be read locked.
   594  func (g *GenericMulticastProtocolState) setDelayTimerForAddressRLocked(groupAddress tcpip.Address, info *multicastGroupState, maxResponseTime time.Duration) {
   595  	if info.state == nonMember {
   596  		return
   597  	}
   598  
   599  	if !g.opts.Protocol.ShouldPerformProtocol(groupAddress) {
   600  		return
   601  	}
   602  
   603  	// As per RFC 2236 section 3 page 3 (for IGMPv2),
   604  	//
   605  	//   If a timer for the group is already unning, it is reset to the random
   606  	//   value only if the requested Max Response Time is less than the remaining
   607  	//   value of the running timer.
   608  	//
   609  	// As per RFC 2710 section 4 page 5 (for MLDv1),
   610  	//
   611  	//   If a timer for any address is already running, it is reset to the new
   612  	//   random value only if the requested Maximum Response Delay is less than
   613  	//   the remaining value of the running timer.
   614  	now := g.opts.Clock.Now()
   615  	if info.state == delayingMember {
   616  		if info.delayedReportJobFiresAt.IsZero() {
   617  			panic(fmt.Sprintf("delayed report unscheduled while in the delaying member state; group = %s", groupAddress))
   618  		}
   619  
   620  		if info.delayedReportJobFiresAt.Sub(now) <= maxResponseTime {
   621  			// The timer is scheduled to fire before the maximum response time so we
   622  			// leave our timer as is.
   623  			return
   624  		}
   625  	}
   626  
   627  	info.state = delayingMember
   628  	info.cancelDelayedReportJob()
   629  	maxResponseTime = g.calculateDelayTimerDuration(maxResponseTime)
   630  	info.delayedReportJob.Schedule(maxResponseTime)
   631  	info.delayedReportJobFiresAt = now.Add(maxResponseTime)
   632  }
   633  
   634  // calculateDelayTimerDuration returns a random time between (0, maxRespTime].
   635  func (g *GenericMulticastProtocolState) calculateDelayTimerDuration(maxRespTime time.Duration) time.Duration {
   636  	// As per RFC 2236 section 3 page 3 (for IGMPv2),
   637  	//
   638  	//   When a host receives a Group-Specific Query, it sets a delay timer to a
   639  	//   random value selected from the range (0, Max Response Time]...
   640  	//
   641  	// As per RFC 2710 section 4 page 6 (for MLDv1),
   642  	//
   643  	//   When a node receives a Multicast-Address-Specific Query, if it is
   644  	//   listening to the queried Multicast Address on the interface from
   645  	//   which the Query was received, it sets a delay timer for that address
   646  	//   to a random value selected from the range [0, Maximum Response Delay],
   647  	//   as above.
   648  	if maxRespTime == 0 {
   649  		return 0
   650  	}
   651  	return time.Duration(g.opts.Rand.Int63n(int64(maxRespTime)))
   652  }