Refresh delayed report timers on query messages
...as per As per RFC 2236 section 3 page 3 (for IGMPv2) and RFC 2710 section 4 page 5 (for MLDv1). See comments in code for more details. Test: ip_test.TestHandleQuery PiperOrigin-RevId: 354603068
This commit is contained in:
parent
0a52b64794
commit
5e2edfb872
|
@ -126,6 +126,16 @@ type multicastGroupState struct {
|
||||||
//
|
//
|
||||||
// Must not be nil.
|
// Must not be nil.
|
||||||
delayedReportJob *tcpip.Job
|
delayedReportJob *tcpip.Job
|
||||||
|
|
||||||
|
// delyedReportJobFiresAt is the time when the delayed report job will fire.
|
||||||
|
//
|
||||||
|
// A zero value indicates that the job is not scheduled.
|
||||||
|
delayedReportJobFiresAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *multicastGroupState) cancelDelayedReportJob() {
|
||||||
|
m.delayedReportJob.Cancel()
|
||||||
|
m.delayedReportJobFiresAt = time.Time{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenericMulticastProtocolOptions holds options for the generic multicast
|
// GenericMulticastProtocolOptions holds options for the generic multicast
|
||||||
|
@ -428,7 +438,7 @@ func (g *GenericMulticastProtocolState) HandleReportLocked(groupAddress tcpip.Ad
|
||||||
// on that interface, it stops its timer and does not send a Report for
|
// on that interface, it stops its timer and does not send a Report for
|
||||||
// that address, thus suppressing duplicate reports on the link.
|
// that address, thus suppressing duplicate reports on the link.
|
||||||
if info, ok := g.memberships[groupAddress]; ok && info.state.isDelayingMember() {
|
if info, ok := g.memberships[groupAddress]; ok && info.state.isDelayingMember() {
|
||||||
info.delayedReportJob.Cancel()
|
info.cancelDelayedReportJob()
|
||||||
info.lastToSendReport = false
|
info.lastToSendReport = false
|
||||||
info.state = idleMember
|
info.state = idleMember
|
||||||
g.memberships[groupAddress] = info
|
g.memberships[groupAddress] = info
|
||||||
|
@ -603,7 +613,7 @@ func (g *GenericMulticastProtocolState) transitionToNonMemberLocked(groupAddress
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info.delayedReportJob.Cancel()
|
info.cancelDelayedReportJob()
|
||||||
g.maybeSendLeave(groupAddress, info.lastToSendReport)
|
g.maybeSendLeave(groupAddress, info.lastToSendReport)
|
||||||
info.lastToSendReport = false
|
info.lastToSendReport = false
|
||||||
info.state = nonMember
|
info.state = nonMember
|
||||||
|
@ -645,14 +655,24 @@ func (g *GenericMulticastProtocolState) setDelayTimerForAddressRLocked(groupAddr
|
||||||
// If a timer for any address is already running, it is reset to the new
|
// If a timer for any address is already running, it is reset to the new
|
||||||
// random value only if the requested Maximum Response Delay is less than
|
// random value only if the requested Maximum Response Delay is less than
|
||||||
// the remaining value of the running timer.
|
// the remaining value of the running timer.
|
||||||
|
now := time.Unix(0 /* seconds */, g.opts.Clock.NowNanoseconds())
|
||||||
if info.state == delayingMember {
|
if info.state == delayingMember {
|
||||||
// TODO: Reset the timer if time remaining is greater than maxResponseTime.
|
if info.delayedReportJobFiresAt.IsZero() {
|
||||||
|
panic(fmt.Sprintf("delayed report unscheduled while in the delaying member state; group = %s", groupAddress))
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.delayedReportJobFiresAt.Sub(now) <= maxResponseTime {
|
||||||
|
// The timer is scheduled to fire before the maximum response time so we
|
||||||
|
// leave our timer as is.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info.state = delayingMember
|
info.state = delayingMember
|
||||||
info.delayedReportJob.Cancel()
|
info.cancelDelayedReportJob()
|
||||||
info.delayedReportJob.Schedule(g.calculateDelayTimerDuration(maxResponseTime))
|
maxResponseTime = g.calculateDelayTimerDuration(maxResponseTime)
|
||||||
|
info.delayedReportJob.Schedule(maxResponseTime)
|
||||||
|
info.delayedReportJobFiresAt = now.Add(maxResponseTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculateDelayTimerDuration returns a random time between (0, maxRespTime].
|
// calculateDelayTimerDuration returns a random time between (0, maxRespTime].
|
||||||
|
|
|
@ -411,37 +411,43 @@ func TestHandleQuery(t *testing.T) {
|
||||||
name string
|
name string
|
||||||
queryAddr tcpip.Address
|
queryAddr tcpip.Address
|
||||||
maxDelay time.Duration
|
maxDelay time.Duration
|
||||||
expectReportsFor []tcpip.Address
|
expectQueriedReportsFor []tcpip.Address
|
||||||
|
expectDelayedReportsFor []tcpip.Address
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "Unpecified empty",
|
name: "Unpecified empty",
|
||||||
queryAddr: "",
|
queryAddr: "",
|
||||||
maxDelay: 0,
|
maxDelay: 0,
|
||||||
expectReportsFor: []tcpip.Address{addr1, addr2},
|
expectQueriedReportsFor: []tcpip.Address{addr1, addr2},
|
||||||
|
expectDelayedReportsFor: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Unpecified any",
|
name: "Unpecified any",
|
||||||
queryAddr: "\x00",
|
queryAddr: "\x00",
|
||||||
maxDelay: 1,
|
maxDelay: 1,
|
||||||
expectReportsFor: []tcpip.Address{addr1, addr2},
|
expectQueriedReportsFor: []tcpip.Address{addr1, addr2},
|
||||||
|
expectDelayedReportsFor: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Specified",
|
name: "Specified",
|
||||||
queryAddr: addr1,
|
queryAddr: addr1,
|
||||||
maxDelay: 2,
|
maxDelay: 2,
|
||||||
expectReportsFor: []tcpip.Address{addr1},
|
expectQueriedReportsFor: []tcpip.Address{addr1},
|
||||||
|
expectDelayedReportsFor: []tcpip.Address{addr2},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Specified all-nodes",
|
name: "Specified all-nodes",
|
||||||
queryAddr: addr3,
|
queryAddr: addr3,
|
||||||
maxDelay: 3,
|
maxDelay: 3,
|
||||||
expectReportsFor: nil,
|
expectQueriedReportsFor: nil,
|
||||||
|
expectDelayedReportsFor: []tcpip.Address{addr1, addr2},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Specified other",
|
name: "Specified other",
|
||||||
queryAddr: addr4,
|
queryAddr: addr4,
|
||||||
maxDelay: 4,
|
maxDelay: 4,
|
||||||
expectReportsFor: nil,
|
expectQueriedReportsFor: nil,
|
||||||
|
expectDelayedReportsFor: []tcpip.Address{addr1, addr2},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -469,20 +475,20 @@ func TestHandleQuery(t *testing.T) {
|
||||||
if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
|
if diff := mgp.check(nil /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
|
||||||
t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
|
t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
// Generic multicast protocol timers are expected to take the job mutex.
|
|
||||||
clock.Advance(maxUnsolicitedReportDelay)
|
|
||||||
if diff := mgp.check([]tcpip.Address{addr1, addr2} /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
|
|
||||||
t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receiving a query should make us schedule a new delayed report if it
|
// Receiving a query should make us reschedule our delayed report timer
|
||||||
// is a query directed at us or a general query.
|
// to some time within the new max response delay.
|
||||||
mgp.handleQuery(test.queryAddr, test.maxDelay)
|
mgp.handleQuery(test.queryAddr, test.maxDelay)
|
||||||
if len(test.expectReportsFor) != 0 {
|
|
||||||
clock.Advance(test.maxDelay)
|
clock.Advance(test.maxDelay)
|
||||||
if diff := mgp.check(test.expectReportsFor /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
|
if diff := mgp.check(test.expectQueriedReportsFor /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
|
||||||
t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
|
t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The groups that were not affected by the query should still send a
|
||||||
|
// report after the max unsolicited report delay.
|
||||||
|
clock.Advance(maxUnsolicitedReportDelay)
|
||||||
|
if diff := mgp.check(test.expectDelayedReportsFor /* sendReportGroupAddresses */, nil /* sendLeaveGroupAddresses */); diff != "" {
|
||||||
|
t.Errorf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should have no more messages to send.
|
// Should have no more messages to send.
|
||||||
|
|
Loading…
Reference in New Issue