Cleanup temp SLAAC address jobs on DAD conflicts

Previously, when DAD would detect a conflict for a temporary address,
the address would be removed but its timers would not be stopped,
resulting in a panic when the removed address's invalidation timer
fired.

While I'm here, remove the check for unicast-ness on removed address
endpoints since multicast addresses are no longer stored in the same
structure as unicast addresses as of ab40e616341aa46d12ca1264b4036fc75b3.

Test: stack_test.TestMixedSLAACAddrConflictRegen
PiperOrigin-RevId: 359344849

This is a cherry pick of af8abf1.

This cherry pick needed to exclude `dadFailure`
from ipv6.endpoint.removePermanentEndpointInnerLocked as the parent
commits do not include 47454095c0da5bf2534391f21f3e034b8f534c72.

Test: fx test //src/connectivity/network/tests/integration
Bug: 70723
Change-Id: I9d0a2cb6f22265caa40db67e53e7f26e512f4ea1
diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go
index ae4a8f5..0883956 100644
--- a/pkg/tcpip/network/ipv6/ipv6.go
+++ b/pkg/tcpip/network/ipv6/ipv6.go
@@ -1385,28 +1385,31 @@
 // Precondition: e.mu must be write locked.
 func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEndpoint, allowSLAACInvalidation bool) *tcpip.Error {
 	addr := addressEndpoint.AddressWithPrefix()
-	unicast := header.IsV6UnicastAddress(addr.Address)
-	if unicast {
-		e.mu.ndp.stopDuplicateAddressDetection(addr.Address)
 
-		// If we are removing an address generated via SLAAC, cleanup
-		// its SLAAC resources and notify the integrator.
-		switch addressEndpoint.ConfigType() {
-		case stack.AddressConfigSlaac:
-			e.mu.ndp.cleanupSLAACAddrResourcesAndNotify(addr, allowSLAACInvalidation)
-		case stack.AddressConfigSlaacTemp:
-			e.mu.ndp.cleanupTempSLAACAddrResourcesAndNotify(addr, allowSLAACInvalidation)
-		}
+	// If we are removing an address generated via SLAAC, cleanup
+	// its SLAAC resources and notify the integrator.
+	switch addressEndpoint.ConfigType() {
+	case stack.AddressConfigSlaac:
+		e.mu.ndp.cleanupSLAACAddrResourcesAndNotify(addr, allowSLAACInvalidation)
+	case stack.AddressConfigSlaacTemp:
+		e.mu.ndp.cleanupTempSLAACAddrResourcesAndNotify(addr)
 	}
 
+	return e.removePermanentEndpointInnerLocked(addressEndpoint)
+}
+
+// removePermanentEndpointInnerLocked is like removePermanentEndpointLocked
+// except it does not cleanup SLAAC address state.
+//
+// Precondition: e.mu must be write locked.
+func (e *endpoint) removePermanentEndpointInnerLocked(addressEndpoint stack.AddressEndpoint) *tcpip.Error {
+	addr := addressEndpoint.AddressWithPrefix()
+	e.mu.ndp.stopDuplicateAddressDetection(addr.Address)
+
 	if err := e.mu.addressableEndpointState.RemovePermanentEndpoint(addressEndpoint); err != nil {
 		return err
 	}
 
-	if !unicast {
-		return nil
-	}
-
 	snmc := header.SolicitedNodeAddr(addr.Address)
 	// The endpoint may have already left the multicast group.
 	if err := e.leaveGroupLocked(snmc); err != nil && err != tcpip.ErrBadLocalAddress {
diff --git a/pkg/tcpip/network/ipv6/ndp.go b/pkg/tcpip/network/ipv6/ndp.go
index 1d8fee5..6a64cc9 100644
--- a/pkg/tcpip/network/ipv6/ndp.go
+++ b/pkg/tcpip/network/ipv6/ndp.go
@@ -1631,9 +1631,11 @@
 	ndp.cleanupSLAACPrefixResources(prefix, state)
 
 	if addressEndpoint := state.stableAddr.addressEndpoint; addressEndpoint != nil {
-		// Since we are already invalidating the prefix, do not invalidate the
-		// prefix when removing the address.
-		if err := ndp.ep.removePermanentEndpointLocked(addressEndpoint, false /* allowSLAACInvalidation */); err != nil {
+		if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
+			ndpDisp.OnAutoGenAddressInvalidated(ndp.ep.nic.ID(), addressEndpoint.AddressWithPrefix())
+		}
+
+		if err := ndp.ep.removePermanentEndpointInnerLocked(addressEndpoint); err != nil {
 			panic(fmt.Sprintf("ndp: error removing stable SLAAC address %s: %s", addressEndpoint.AddressWithPrefix(), err))
 		}
 	}
@@ -1690,28 +1692,19 @@
 //
 // The IPv6 endpoint that ndp belongs to MUST be locked.
 func (ndp *ndpState) invalidateTempSLAACAddr(tempAddrs map[tcpip.Address]tempSLAACAddrState, tempAddr tcpip.Address, tempAddrState tempSLAACAddrState) {
-	// Since we are already invalidating the address, do not invalidate the
-	// address when removing the address.
-	if err := ndp.ep.removePermanentEndpointLocked(tempAddrState.addressEndpoint, false /* allowSLAACInvalidation */); err != nil {
+	ndp.cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs, tempAddr, tempAddrState)
+
+	if err := ndp.ep.removePermanentEndpointInnerLocked(tempAddrState.addressEndpoint); err != nil {
 		panic(fmt.Sprintf("error removing temporary SLAAC address %s: %s", tempAddrState.addressEndpoint.AddressWithPrefix(), err))
 	}
-
-	ndp.cleanupTempSLAACAddrResources(tempAddrs, tempAddr, tempAddrState)
 }
 
 // cleanupTempSLAACAddrResourcesAndNotify cleans up an invalidated temporary
-// SLAAC address's resources from ndp.
+// SLAAC address's resources from ndp and notifies the NDP dispatcher that the
+// address was invalidated.
 //
 // The IPv6 endpoint that ndp belongs to MUST be locked.
-func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPrefix, invalidateAddr bool) {
-	if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
-		ndpDisp.OnAutoGenAddressInvalidated(ndp.ep.nic.ID(), addr)
-	}
-
-	if !invalidateAddr {
-		return
-	}
-
+func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotify(addr tcpip.AddressWithPrefix) {
 	prefix := addr.Subnet()
 	state, ok := ndp.slaacPrefixes[prefix]
 	if !ok {
@@ -1723,14 +1716,19 @@
 		panic(fmt.Sprintf("ndp: must have a tempAddr entry to clean up temp addr %s resources", addr))
 	}
 
-	ndp.cleanupTempSLAACAddrResources(state.tempAddrs, addr.Address, tempAddrState)
+	ndp.cleanupTempSLAACAddrResourcesAndNotifyInner(state.tempAddrs, addr.Address, tempAddrState)
 }
 
-// cleanupTempSLAACAddrResourcesAndNotify cleans up a temporary SLAAC address's
-// jobs and entry.
+// cleanupTempSLAACAddrResourcesAndNotifyInner is like
+// cleanupTempSLAACAddrResourcesAndNotify except it does not lookup the
+// temporary address's state in ndp - it assumes the passed state is valid.
 //
 // The IPv6 endpoint that ndp belongs to MUST be locked.
-func (ndp *ndpState) cleanupTempSLAACAddrResources(tempAddrs map[tcpip.Address]tempSLAACAddrState, tempAddr tcpip.Address, tempAddrState tempSLAACAddrState) {
+func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs map[tcpip.Address]tempSLAACAddrState, tempAddr tcpip.Address, tempAddrState tempSLAACAddrState) {
+	if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
+		ndpDisp.OnAutoGenAddressInvalidated(ndp.ep.nic.ID(), tempAddrState.addressEndpoint.AddressWithPrefix())
+	}
+
 	tempAddrState.addressEndpoint.DecRef()
 	tempAddrState.addressEndpoint = nil
 	tempAddrState.deprecationJob.Cancel()