From a5ac059e279b602ad42caeb4c48d09cb566e6eba Mon Sep 17 00:00:00 2001 From: Ghanan Gowripalan Date: Wed, 8 Feb 2023 16:46:07 -0800 Subject: [PATCH] Return current IGMP/MLD version Updates #8346 PiperOrigin-RevId: 508218820 --- .../internal/ip/generic_multicast_protocol.go | 53 ++++++++++--------- .../ip/generic_multicast_protocol_test.go | 18 ++++++- pkg/tcpip/network/ipv4/igmp.go | 22 ++++++-- pkg/tcpip/network/ipv4/igmp_test.go | 14 ++++- pkg/tcpip/network/ipv4/ipv4.go | 13 +++++ pkg/tcpip/network/ipv6/ipv6.go | 7 +++ pkg/tcpip/network/ipv6/mld.go | 18 ++++++- pkg/tcpip/network/ipv6/mld_test.go | 11 +++- 8 files changed, 123 insertions(+), 33 deletions(-) diff --git a/pkg/tcpip/network/internal/ip/generic_multicast_protocol.go b/pkg/tcpip/network/internal/ip/generic_multicast_protocol.go index fb2e642ef..a6f32017d 100644 --- a/pkg/tcpip/network/internal/ip/generic_multicast_protocol.go +++ b/pkg/tcpip/network/internal/ip/generic_multicast_protocol.go @@ -292,38 +292,45 @@ type GenericMulticastProtocolState struct { stateChangedReportV2TimerSet bool } +// GetV1ModeLocked returns the V1 configuration. +// +// Precondition: g.protocolMU must be read locked. +func (g *GenericMulticastProtocolState) GetV1ModeLocked() bool { + switch g.mode { + case protocolModeV2, protocolModeV1Compatibility: + return false + case protocolModeV1: + return true + default: + panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) + } +} + +func (g *GenericMulticastProtocolState) stopModeTimer() { + if g.modeTimer != nil { + g.modeTimer.Stop() + } +} + // SetV1ModeLocked sets the V1 configuration. // // Returns the previous configuration. // // Precondition: g.protocolMU must be locked. func (g *GenericMulticastProtocolState) SetV1ModeLocked(v bool) bool { + if g.GetV1ModeLocked() == v { + return v + } + if v { - switch g.mode { - case protocolModeV2: - g.cancelV2ReportTimers() - case protocolModeV1Compatibility: - g.modeTimer.Stop() - case protocolModeV1: - // Already in V1 mode; nothing to do. - return true - default: - panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) - } + g.stopModeTimer() + g.cancelV2ReportTimers() g.mode = protocolModeV1 return false } - switch g.mode { - case protocolModeV2, protocolModeV1Compatibility: - // Not in V1 mode; nothing to do. - return false - case protocolModeV1: - g.mode = protocolModeV2 - return true - default: - panic(fmt.Sprintf("unrecognized mode = %d", g.mode)) - } + g.mode = protocolModeV2 + return true } func (g *GenericMulticastProtocolState) cancelV2ReportTimers() { @@ -375,9 +382,7 @@ func (g *GenericMulticastProtocolState) MakeAllNonMemberLocked() { return } - if g.modeTimer != nil { - g.modeTimer.Stop() - } + g.stopModeTimer() g.cancelV2ReportTimers() var v2ReportBuilder MulticastGroupProtocolV2ReportBuilder diff --git a/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go b/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go index e145ab7ac..71728b8eb 100644 --- a/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go +++ b/pkg/tcpip/network/internal/ip/generic_multicast_protocol_test.go @@ -88,6 +88,12 @@ func (m *mockMulticastGroupProtocol) setV1Mode(v bool) bool { return m.mu.genericMulticastGroup.SetV1ModeLocked(v) } +func (m *mockMulticastGroupProtocol) getV1Mode() bool { + m.mu.RLock() + defer m.mu.RUnlock() + return m.mu.genericMulticastGroup.GetV1ModeLocked() +} + func (m *mockMulticastGroupProtocol) joinGroup(addr tcpip.Address) { m.mu.Lock() defer m.mu.Unlock() @@ -1506,7 +1512,7 @@ func TestQueuedPackets(t *testing.T) { } } -func TestSetV1Mode(t *testing.T) { +func TestGetSetV1Mode(t *testing.T) { clock := faketime.NewManualClock() mgp := mockMulticastGroupProtocol{t: t} mgp.init(ip.GenericMulticastProtocolOptions{ @@ -1515,6 +1521,10 @@ func TestSetV1Mode(t *testing.T) { MaxUnsolicitedReportDelay: maxUnsolicitedReportDelay, }, false /* v1Compatibility */) + if mgp.getV1Mode() { + t.Error("got mgp.getV1Mode() = true, want = false") + } + mgp.joinGroup(addr1) if diff := mgp.check(checkFields{sentV2Reports: []mockReportV2{{records: []mockReportV2Record{ { @@ -1528,6 +1538,9 @@ func TestSetV1Mode(t *testing.T) { if mgp.setV1Mode(true) { t.Error("got mgp.setV1Mode(true) = true, want = false") } + if !mgp.getV1Mode() { + t.Error("got mgp.getV1Mode() = false, want = true") + } mgp.joinGroup(addr2) if diff := mgp.check(checkFields{sendReportGroupAddresses: []tcpip.Address{addr2}}); diff != "" { t.Fatalf("mockMulticastGroupProtocol mismatch (-want +got):\n%s", diff) @@ -1536,6 +1549,9 @@ func TestSetV1Mode(t *testing.T) { if !mgp.setV1Mode(false) { t.Error("got mgp.setV1Mode(false) = false, want = true") } + if mgp.getV1Mode() { + t.Error("got mgp.getV1Mode() = true, want = false") + } mgp.joinGroup(addr3) if diff := mgp.check(checkFields{sentV2Reports: []mockReportV2{{records: []mockReportV2Record{ { diff --git a/pkg/tcpip/network/ipv4/igmp.go b/pkg/tcpip/network/ipv4/igmp.go index 356225a0f..0f976e2a2 100644 --- a/pkg/tcpip/network/ipv4/igmp.go +++ b/pkg/tcpip/network/ipv4/igmp.go @@ -80,10 +80,13 @@ const ( // IGMPEndpoint is a network endpoint that supports IGMP. type IGMPEndpoint interface { - // Sets the IGMP version. + // SetIGMPVersion sets the IGMP version. // // Returns the previous IGMP version. SetIGMPVersion(IGMPVersion) IGMPVersion + + // GetIGMPVersion returns the IGMP version. + GetIGMPVersion() IGMPVersion } // IGMPOptions holds options for IGMP. @@ -622,15 +625,26 @@ func (igmp *igmpState) setVersion(v IGMPVersion) IGMPVersion { panic(fmt.Sprintf("unrecognized version = %d", v)) } - switch prev { + return toIGMPVersion(prev, prevGenericModeV1) +} + +func toIGMPVersion(mode protocolMode, genericV1 bool) IGMPVersion { + switch mode { case protocolModeV2OrV3, protocolModeV1Compatibility: - if prevGenericModeV1 { + if genericV1 { return IGMPVersion2 } return IGMPVersion3 case protocolModeV1: return IGMPVersion1 default: - panic(fmt.Sprintf("unrecognized mode = %d", igmp.mode)) + panic(fmt.Sprintf("unrecognized mode = %d", mode)) } } + +// getVersion returns the IGMP version. +// +// +checklocksread:igmp.ep.mu +func (igmp *igmpState) getVersion() IGMPVersion { + return toIGMPVersion(igmp.mode, igmp.genericMulticastProtocol.GetV1ModeLocked()) +} diff --git a/pkg/tcpip/network/ipv4/igmp_test.go b/pkg/tcpip/network/ipv4/igmp_test.go index c7e3f1e9c..f6d95afa2 100644 --- a/pkg/tcpip/network/ipv4/igmp_test.go +++ b/pkg/tcpip/network/ipv4/igmp_test.go @@ -509,7 +509,7 @@ func TestIGMPPacketValidation(t *testing.T) { } } -func TestSetIGMPVersion(t *testing.T) { +func TestGetSetIGMPVersion(t *testing.T) { const nicID = 1 c := newIGMPTestContext(t, true /* igmpEnabled */) @@ -525,6 +525,9 @@ func TestSetIGMPVersion(t *testing.T) { if !ok { t.Fatalf("got (%T).(%T) = (_, false), want = (_ true)", ep, igmpEP) } + if got := igmpEP.GetIGMPVersion(); got != ipv4.IGMPVersion3 { + t.Errorf("got igmpEP.GetIGMPVersion() = %d, want = %d", got, ipv4.IGMPVersion3) + } protocolAddr := tcpip.ProtocolAddress{ Protocol: ipv4.ProtocolNumber, @@ -547,6 +550,9 @@ func TestSetIGMPVersion(t *testing.T) { if got := igmpEP.SetIGMPVersion(ipv4.IGMPVersion2); got != ipv4.IGMPVersion3 { t.Errorf("got igmpEP.SetIGMPVersion(%d) = %d, want = %d", ipv4.IGMPVersion2, got, ipv4.IGMPVersion3) } + if got := igmpEP.GetIGMPVersion(); got != ipv4.IGMPVersion2 { + t.Errorf("got igmpEP.GetIGMPVersion() = %d, want = %d", got, ipv4.IGMPVersion2) + } if err := s.JoinGroup(ipv4.ProtocolNumber, nicID, multicastAddr2); err != nil { t.Fatalf("JoinGroup(ipv4, nic, %s) = %s", multicastAddr2, err) } @@ -560,6 +566,9 @@ func TestSetIGMPVersion(t *testing.T) { if got := igmpEP.SetIGMPVersion(ipv4.IGMPVersion1); got != ipv4.IGMPVersion2 { t.Errorf("got igmpEP.SetIGMPVersion(%d) = %d, want = %d", ipv4.IGMPVersion1, got, ipv4.IGMPVersion2) } + if got := igmpEP.GetIGMPVersion(); got != ipv4.IGMPVersion1 { + t.Errorf("got igmpEP.GetIGMPVersion() = %d, want = %d", got, ipv4.IGMPVersion1) + } if err := s.JoinGroup(ipv4.ProtocolNumber, nicID, multicastAddr3); err != nil { t.Fatalf("JoinGroup(ipv4, nic, %s) = %s", multicastAddr3, err) } @@ -573,6 +582,9 @@ func TestSetIGMPVersion(t *testing.T) { if got := igmpEP.SetIGMPVersion(ipv4.IGMPVersion3); got != ipv4.IGMPVersion1 { t.Errorf("got igmpEP.SetIGMPVersion(%d) = %d, want = %d", ipv4.IGMPVersion3, got, ipv4.IGMPVersion1) } + if got := igmpEP.GetIGMPVersion(); got != ipv4.IGMPVersion3 { + t.Errorf("got igmpEP.GetIGMPVersion() = %d, want = %d", got, ipv4.IGMPVersion3) + } if err := s.JoinGroup(ipv4.ProtocolNumber, nicID, multicastAddr4); err != nil { t.Fatalf("JoinGroup(ipv4, nic, %s) = %s", multicastAddr4, err) } diff --git a/pkg/tcpip/network/ipv4/ipv4.go b/pkg/tcpip/network/ipv4/ipv4.go index 53930ff82..3d8b8208e 100644 --- a/pkg/tcpip/network/ipv4/ipv4.go +++ b/pkg/tcpip/network/ipv4/ipv4.go @@ -117,12 +117,25 @@ func (e *endpoint) SetIGMPVersion(v IGMPVersion) IGMPVersion { return e.setIGMPVersionLocked(v) } +// GetIGMPVersion implements IGMPEndpoint. +func (e *endpoint) GetIGMPVersion() IGMPVersion { + e.mu.RLock() + defer e.mu.RUnlock() + return e.getIGMPVersionLocked() +} + // +checklocks:e.mu // +checklocksalias:e.igmp.ep.mu=e.mu func (e *endpoint) setIGMPVersionLocked(v IGMPVersion) IGMPVersion { return e.igmp.setVersion(v) } +// +checklocksread:e.mu +// +checklocksalias:e.igmp.ep.mu=e.mu +func (e *endpoint) getIGMPVersionLocked() IGMPVersion { + return e.igmp.getVersion() +} + // HandleLinkResolutionFailure implements stack.LinkResolvableNetworkEndpoint. func (e *endpoint) HandleLinkResolutionFailure(pkt stack.PacketBufferPtr) { // If we are operating as a router, return an ICMP error to the original diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go index c3b1a88b5..7dd003cc1 100644 --- a/pkg/tcpip/network/ipv6/ipv6.go +++ b/pkg/tcpip/network/ipv6/ipv6.go @@ -364,6 +364,13 @@ func (e *endpoint) SetMLDVersion(v MLDVersion) MLDVersion { return e.mu.mld.setVersion(v) } +// GetMLDVersion implements MLDEndpoint. +func (e *endpoint) GetMLDVersion() MLDVersion { + e.mu.RLock() + defer e.mu.RUnlock() + return e.mu.mld.getVersion() +} + // SetNDPConfigurations implements NDPEndpoint. func (e *endpoint) SetNDPConfigurations(c NDPConfigurations) { c.validate() diff --git a/pkg/tcpip/network/ipv6/mld.go b/pkg/tcpip/network/ipv6/mld.go index 4444bb424..f927dbd53 100644 --- a/pkg/tcpip/network/ipv6/mld.go +++ b/pkg/tcpip/network/ipv6/mld.go @@ -47,10 +47,13 @@ const ( // MLDEndpoint is a network endpoint that supports MLD. type MLDEndpoint interface { - // Sets the MLD version. + // SetMLDVersions sets the MLD version. // // Returns the previous MLD version. SetMLDVersion(MLDVersion) MLDVersion + + // GetMLDVersion returns the MLD version. + GetMLDVersion() MLDVersion } // MLDOptions holds options for MLD. @@ -334,12 +337,23 @@ func (mld *mldState) setVersion(v MLDVersion) MLDVersion { panic(fmt.Sprintf("unrecognized version = %d", v)) } - if prev { + return toMLDVersion(prev) +} + +func toMLDVersion(v1Generic bool) MLDVersion { + if v1Generic { return MLDVersion1 } return MLDVersion2 } +// getVersion returns the MLD version. +// +// Precondition: mld.ep.mu must be read locked. +func (mld *mldState) getVersion() MLDVersion { + return toMLDVersion(mld.genericMulticastProtocol.GetV1ModeLocked()) +} + // writePacket assembles and sends an MLD packet. // // Precondition: mld.ep.mu must be read locked. diff --git a/pkg/tcpip/network/ipv6/mld_test.go b/pkg/tcpip/network/ipv6/mld_test.go index 0aeab2f21..01434c5f2 100644 --- a/pkg/tcpip/network/ipv6/mld_test.go +++ b/pkg/tcpip/network/ipv6/mld_test.go @@ -759,7 +759,7 @@ func TestMLDSkipProtocol(t *testing.T) { } } -func TestSetMLDVersion(t *testing.T) { +func TestGetSetMLDVersion(t *testing.T) { const nicID = 1 c := newMLDTestContext() @@ -781,6 +781,9 @@ func TestSetMLDVersion(t *testing.T) { if !ok { t.Fatalf("got (%T).(%T) = (_, false), want = (_ true)", ep, mldEP) } + if got := mldEP.GetMLDVersion(); got != ipv6.MLDVersion2 { + t.Errorf("got mldEP.GetMLDVersion() = %d, want = %d", got, ipv6.MLDVersion2) + } protocolAddr := tcpip.ProtocolAddress{ Protocol: ipv6.ProtocolNumber, @@ -799,6 +802,9 @@ func TestSetMLDVersion(t *testing.T) { if got := mldEP.SetMLDVersion(ipv6.MLDVersion1); got != ipv6.MLDVersion2 { t.Errorf("got mldEP.SetMLDVersion(%d) = %d, want = %d", ipv6.MLDVersion1, got, ipv6.MLDVersion2) } + if got := mldEP.GetMLDVersion(); got != ipv6.MLDVersion1 { + t.Errorf("got mldEP.GetMLDVersion() = %d, want = %d", got, ipv6.MLDVersion1) + } if err := s.JoinGroup(ipv6.ProtocolNumber, nicID, globalMulticastAddr); err != nil { t.Fatalf("s.JoinGroup(%d, %d, %s): %s", ipv6.ProtocolNumber, nicID, globalMulticastAddr, err) } @@ -812,6 +818,9 @@ func TestSetMLDVersion(t *testing.T) { if got := mldEP.SetMLDVersion(ipv6.MLDVersion2); got != ipv6.MLDVersion1 { t.Errorf("got mldEP.SetMLDVersion(%d) = %d, want = %d", ipv6.MLDVersion2, got, ipv6.MLDVersion1) } + if got := mldEP.GetMLDVersion(); got != ipv6.MLDVersion2 { + t.Errorf("got mldEP.GetMLDVersion() = %d, want = %d", got, ipv6.MLDVersion2) + } if err := s.LeaveGroup(ipv6.ProtocolNumber, nicID, globalMulticastAddr); err != nil { t.Fatalf("s.LeaveGroup(%d, %d, %s): %s", ipv6.ProtocolNumber, nicID, globalMulticastAddr, err) }