diff options
author | Ghanan Gowripalan <ghanan@google.com> | 2021-02-24 12:30:20 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-02-24 12:32:20 -0800 |
commit | fcd4ff4fca31e3c594b30aff6b008e7af4c7c1a5 (patch) | |
tree | 8de6b5c714b5955d31431a1625e0762f94440415 | |
parent | ba4dfa7172a00f8b104a75a4655fe3de1e4a94c9 (diff) |
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 27ee4fe76ad586ac8751951a842b3681f93.
Test: stack_test.TestMixedSLAACAddrConflictRegen
PiperOrigin-RevId: 359344849
-rw-r--r-- | pkg/tcpip/network/ipv6/ipv6.go | 34 | ||||
-rw-r--r-- | pkg/tcpip/network/ipv6/ndp.go | 42 | ||||
-rw-r--r-- | pkg/tcpip/stack/ndp_test.go | 50 |
3 files changed, 67 insertions, 59 deletions
diff --git a/pkg/tcpip/network/ipv6/ipv6.go b/pkg/tcpip/network/ipv6/ipv6.go index c5c3ef882..b16a2d322 100644 --- a/pkg/tcpip/network/ipv6/ipv6.go +++ b/pkg/tcpip/network/ipv6/ipv6.go @@ -1441,28 +1441,30 @@ func (e *endpoint) RemovePermanentAddress(addr tcpip.Address) tcpip.Error { // Precondition: e.mu must be write locked. func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEndpoint, allowSLAACInvalidation, dadFailure bool) tcpip.Error { addr := addressEndpoint.AddressWithPrefix() - unicast := header.IsV6UnicastAddress(addr.Address) - if unicast { - e.mu.ndp.stopDuplicateAddressDetection(addr.Address, dadFailure) - - // 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, dadFailure) +} + +// 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, dadFailure bool) tcpip.Error { + addr := addressEndpoint.AddressWithPrefix() + e.mu.ndp.stopDuplicateAddressDetection(addr.Address, dadFailure) + if err := e.mu.addressableEndpointState.RemovePermanentEndpoint(addressEndpoint); err != nil { return err } - if !unicast { - return nil - } - snmc := header.SolicitedNodeAddr(addr.Address) err := e.leaveGroupLocked(snmc) // The endpoint may have already left the multicast group. diff --git a/pkg/tcpip/network/ipv6/ndp.go b/pkg/tcpip/network/ipv6/ndp.go index 53c043dcd..721269c58 100644 --- a/pkg/tcpip/network/ipv6/ndp.go +++ b/pkg/tcpip/network/ipv6/ndp.go @@ -1497,9 +1497,11 @@ func (ndp *ndpState) invalidateSLAACPrefix(prefix tcpip.Subnet, state slaacPrefi 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 */, false /* dadFailure */); 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, false /* dadFailure */); err != nil { panic(fmt.Sprintf("ndp: error removing stable SLAAC address %s: %s", addressEndpoint.AddressWithPrefix(), err)) } } @@ -1556,28 +1558,19 @@ func (ndp *ndpState) cleanupSLAACPrefixResources(prefix tcpip.Subnet, state slaa // // 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 */, false /* dadFailure */); err != nil { + ndp.cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs, tempAddr, tempAddrState) + + if err := ndp.ep.removePermanentEndpointInnerLocked(tempAddrState.addressEndpoint, false /* dadFailure */); 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 { @@ -1589,14 +1582,19 @@ func (ndp *ndpState) cleanupTempSLAACAddrResourcesAndNotify(addr tcpip.AddressWi 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() diff --git a/pkg/tcpip/stack/ndp_test.go b/pkg/tcpip/stack/ndp_test.go index 3b6ba9509..740bdac28 100644 --- a/pkg/tcpip/stack/ndp_test.go +++ b/pkg/tcpip/stack/ndp_test.go @@ -2602,11 +2602,13 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) { addrs []tcpip.AddressWithPrefix tempAddrs bool initialExpect tcpip.AddressWithPrefix + maxAddrs int nicNameFromID func(tcpip.NICID, string) string }{ { - name: "Stable addresses with opaque IIDs", - addrs: stableAddrsWithOpaqueIID[:], + name: "Stable addresses with opaque IIDs", + addrs: stableAddrsWithOpaqueIID[:], + maxAddrs: 1, nicNameFromID: func(tcpip.NICID, string) string { return nicName }, @@ -2616,6 +2618,7 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) { addrs: tempAddrsWithOpaqueIID[:], tempAddrs: true, initialExpect: stableAddrsWithOpaqueIID[0], + maxAddrs: 1 /* initial (stable) address */ + maxSLAACAddrLocalRegenAttempts, nicNameFromID: func(tcpip.NICID, string) string { return nicName }, @@ -2624,21 +2627,22 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) { name: "Temporary addresses with modified EUI64", addrs: tempAddrsWithModifiedEUI64[:], tempAddrs: true, + maxAddrs: 1 /* initial (stable) address */ + maxSLAACAddrLocalRegenAttempts, initialExpect: stableAddrWithModifiedEUI64, }, } for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - t.Parallel() - ndpDisp := ndpDispatcher{ - autoGenAddrC: make(chan ndpAutoGenAddrEvent, 2), + // We may receive a deprecated and invalidated event for each SLAAC + // address that is assigned. + autoGenAddrC: make(chan ndpAutoGenAddrEvent, test.maxAddrs*2), } e := channel.New(0, 1280, linkAddr1) + clock := faketime.NewManualClock() s := stack.New(stack.Options{ + Clock: clock, NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{ NDPConfigs: ipv6.NDPConfigurations{ HandleRAs: true, @@ -2664,6 +2668,7 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) { t.Fatalf("CreateNIC(%d, _) = %s", nicID, err) } + manuallyAssignedAddresses := make(map[tcpip.Address]struct{}) for j := 0; j < len(test.addrs)-1; j++ { // The NIC will not attempt to generate an address in response to a // NIC-local conflict after some maximum number of attempts. We skip @@ -2677,6 +2682,8 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) { if err := s.AddAddress(nicID, ipv6.ProtocolNumber, test.addrs[j].Address); err != nil { t.Fatalf("s.AddAddress(%d, %d, %s): %s", nicID, ipv6.ProtocolNumber, test.addrs[j].Address, err) } + + manuallyAssignedAddresses[test.addrs[j].Address] = struct{}{} } expectAutoGenAddrEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) { @@ -2695,26 +2702,17 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) { expectAutoGenAddrAsyncEvent := func(addr tcpip.AddressWithPrefix, eventType ndpAutoGenAddrEventType) { t.Helper() - select { - case e := <-ndpDisp.autoGenAddrC: - if diff := checkAutoGenAddrEvent(e, addr, eventType); diff != "" { - t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff) - } - case <-time.After(defaultAsyncPositiveEventTimeout): - t.Fatal("timed out waiting for addr auto gen event") + if diff := checkAutoGenAddrEvent(<-ndpDisp.autoGenAddrC, addr, eventType); diff != "" { + t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff) } } expectDADEventAsync := func(addr tcpip.Address) { t.Helper() - select { - case e := <-ndpDisp.dadC: - if diff := checkDADEvent(e, nicID, addr, true, nil); diff != "" { - t.Errorf("dad event mismatch (-want +got):\n%s", diff) - } - case <-time.After(dupAddrTransmits*retransmitTimer + defaultAsyncPositiveEventTimeout): - t.Fatal("timed out waiting for DAD event") + clock.Advance(dupAddrTransmits * retransmitTimer) + if diff := checkDADEvent(<-ndpDisp.dadC, nicID, addr, true, nil); diff != "" { + t.Errorf("dad event mismatch (-want +got):\n%s", diff) } } @@ -2762,6 +2760,16 @@ func TestMixedSLAACAddrConflictRegen(t *testing.T) { t.Fatalf("unexpected auto gen addr event = %+v", e) default: } + + // Wait for all the SLAAC addresses to be invalidated. + clock.Advance(lifetimeSeconds * time.Second) + gotAddresses := make(map[tcpip.Address]struct{}) + for _, a := range s.NICInfo()[nicID].ProtocolAddresses { + gotAddresses[a.AddressWithPrefix.Address] = struct{}{} + } + if diff := cmp.Diff(manuallyAssignedAddresses, gotAddresses); diff != "" { + t.Fatalf("assigned addresses mismatch (-want +got):\n%s", diff) + } }) } } |