From 45fe9fe9c6fa92954a5017b98835eac5fd8d3987 Mon Sep 17 00:00:00 2001 From: Ghanan Gowripalan Date: Fri, 29 Jan 2021 16:09:14 -0800 Subject: Clear IGMPv1 present flag on NIC down This is dynamic state that can be re-learned when the NIC comes back up. Test: ipv4_test.TestIgmpV1Present PiperOrigin-RevId: 354630921 --- pkg/tcpip/network/ipv4/igmp.go | 5 +++ pkg/tcpip/network/ipv4/igmp_test.go | 61 +++++++++++++++++++++++++------------ pkg/tcpip/network/ipv4/ipv4.go | 6 ++++ 3 files changed, 52 insertions(+), 20 deletions(-) (limited to 'pkg') diff --git a/pkg/tcpip/network/ipv4/igmp.go b/pkg/tcpip/network/ipv4/igmp.go index 4cd0b3256..acc126c3b 100644 --- a/pkg/tcpip/network/ipv4/igmp.go +++ b/pkg/tcpip/network/ipv4/igmp.go @@ -215,6 +215,11 @@ func (igmp *igmpState) setV1Present(v bool) { } } +func (igmp *igmpState) resetV1Present() { + igmp.igmpV1Job.Cancel() + igmp.setV1Present(false) +} + // handleMembershipQuery handles a membership query. // // Precondition: igmp.ep.mu must be locked. diff --git a/pkg/tcpip/network/ipv4/igmp_test.go b/pkg/tcpip/network/ipv4/igmp_test.go index 1ee573ac8..95fd75ab7 100644 --- a/pkg/tcpip/network/ipv4/igmp_test.go +++ b/pkg/tcpip/network/ipv4/igmp_test.go @@ -101,10 +101,10 @@ func createAndInjectIGMPPacket(e *channel.Endpoint, igmpType header.IGMPType, ma }) } -// TestIgmpV1Present tests the handling of the case where an IGMPv1 router is -// present on the network. The IGMP stack will then send IGMPv1 Membership -// reports for backwards compatibility. -func TestIgmpV1Present(t *testing.T) { +// TestIGMPV1Present tests the node's ability to fallback to V1 when a V1 +// router is detected. V1 present status is expected to be reset when the NIC +// cycles. +func TestIGMPV1Present(t *testing.T) { e, s, clock := createStack(t, true) if err := s.AddAddress(nicID, ipv4.ProtocolNumber, addr); err != nil { t.Fatalf("AddAddress(%d, %d, %s): %s", nicID, ipv4.ProtocolNumber, addr, err) @@ -116,14 +116,16 @@ func TestIgmpV1Present(t *testing.T) { // This NIC will send an IGMPv2 report immediately, before this test can get // the IGMPv1 General Membership Query in. - p, ok := e.Read() - if !ok { - t.Fatal("unable to Read IGMP packet, expected V2MembershipReport") - } - if got := s.Stats().IGMP.PacketsSent.V2MembershipReport.Value(); got != 1 { - t.Fatalf("got V2MembershipReport messages sent = %d, want = 1", got) + { + p, ok := e.Read() + if !ok { + t.Fatal("unable to Read IGMP packet, expected V2MembershipReport") + } + if got := s.Stats().IGMP.PacketsSent.V2MembershipReport.Value(); got != 1 { + t.Fatalf("got V2MembershipReport messages sent = %d, want = 1", got) + } + validateIgmpPacket(t, p, multicastAddr, header.IGMPv2MembershipReport, 0, multicastAddr) } - validateIgmpPacket(t, p, multicastAddr, header.IGMPv2MembershipReport, 0, multicastAddr) if t.Failed() { t.FailNow() } @@ -145,19 +147,38 @@ func TestIgmpV1Present(t *testing.T) { // Verify the solicited Membership Report is sent. Now that this NIC has seen // an IGMPv1 query, it should send an IGMPv1 Membership Report. - p, ok = e.Read() - if ok { + if p, ok := e.Read(); ok { t.Fatalf("sent unexpected packet, expected V1MembershipReport only after advancing the clock = %+v", p.Pkt) } clock.Advance(ipv4.UnsolicitedReportIntervalMax) - p, ok = e.Read() - if !ok { - t.Fatal("unable to Read IGMP packet, expected V1MembershipReport") - } - if got := s.Stats().IGMP.PacketsSent.V1MembershipReport.Value(); got != 1 { - t.Fatalf("got V1MembershipReport messages sent = %d, want = 1", got) + { + p, ok := e.Read() + if !ok { + t.Fatal("unable to Read IGMP packet, expected V1MembershipReport") + } + if got := s.Stats().IGMP.PacketsSent.V1MembershipReport.Value(); got != 1 { + t.Fatalf("got V1MembershipReport messages sent = %d, want = 1", got) + } + validateIgmpPacket(t, p, multicastAddr, header.IGMPv1MembershipReport, 0, multicastAddr) + } + + // Cycling the interface should reset the V1 present flag. + if err := s.DisableNIC(nicID); err != nil { + t.Fatalf("s.DisableNIC(%d): %s", nicID, err) + } + if err := s.EnableNIC(nicID); err != nil { + t.Fatalf("s.EnableNIC(%d): %s", nicID, err) + } + { + p, ok := e.Read() + if !ok { + t.Fatal("unable to Read IGMP packet, expected V2MembershipReport") + } + if got := s.Stats().IGMP.PacketsSent.V2MembershipReport.Value(); got != 2 { + t.Fatalf("got V2MembershipReport messages sent = %d, want = 2", got) + } + validateIgmpPacket(t, p, multicastAddr, header.IGMPv2MembershipReport, 0, multicastAddr) } - validateIgmpPacket(t, p, multicastAddr, header.IGMPv1MembershipReport, 0, multicastAddr) } func TestSendQueuedIGMPReports(t *testing.T) { diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index e5c80699d..b0703715a 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -229,6 +229,12 @@ func (e *endpoint) disableLocked() { panic(fmt.Sprintf("unexpected error when removing address = %s: %s", ipv4BroadcastAddr.Address, err)) } + // Reset the IGMP V1 present flag. + // + // If the node comes back up on the same network, it will re-learn that it + // needs to perform IGMPv1. + e.mu.igmp.resetV1Present() + if !e.setEnabled(false) { panic("should have only done work to disable the endpoint if it was enabled") } -- cgit v1.2.3