summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip')
-rw-r--r--pkg/tcpip/network/ipv4/igmp.go5
-rw-r--r--pkg/tcpip/network/ipv4/igmp_test.go61
-rw-r--r--pkg/tcpip/network/ipv4/ipv4.go6
3 files changed, 52 insertions, 20 deletions
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")
}