summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGhanan Gowripalan <ghanan@google.com>2021-02-24 12:30:20 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-24 12:32:20 -0800
commitfcd4ff4fca31e3c594b30aff6b008e7af4c7c1a5 (patch)
tree8de6b5c714b5955d31431a1625e0762f94440415
parentba4dfa7172a00f8b104a75a4655fe3de1e4a94c9 (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.go34
-rw-r--r--pkg/tcpip/network/ipv6/ndp.go42
-rw-r--r--pkg/tcpip/stack/ndp_test.go50
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)
+ }
})
}
}