Introduce and Implement AddressDispatcher

Introduce the AddressDispatcher interface which integrators can
provide an implementation at the time of adding an address
to receive callbacks when address properties change and when the
address is removed. Modify `NDPDispatcher`'s callback when a SLAAC
address is added to receive an implementation of `AddressDispatcher`.

Added informational preferred and valid lifetime fields to
`AddressProperties` so they can be set when adding the address; and a
way to update said lifetimes.

Added a means to disable an `AddressableEndpointState` and each
individual `addressState`, so that the `AddressDisabled` assignment
state can be reported to integrators.

Added a configurable option to `AddressableEndpointState` which
determines whether addresses of kind `PermanentDisabled` are included
in the return value of `PrimaryAddresses` and `PermanentAddresses`. This
option is set such that IPv4 addresses are returned, while IPv6
addresses are hidden, when the NIC is disabled. This is a change in
behavior for IPv6, but is consistent with behavior on Linux.

Modified tests in `ndp_test` to use the new AddressDispatcher. Fixed
some bugs along the way.

PiperOrigin-RevId: 459658009
This commit is contained in:
Tony Gong
2022-07-07 19:58:25 -07:00
committed by gVisor bot
parent 7103ddb238
commit be6ffa78e4
15 changed files with 1551 additions and 665 deletions
+15 -2
View File
@@ -142,7 +142,7 @@ func (p *protocol) NewEndpoint(nic stack.NetworkInterface, dispatcher stack.Tran
protocol: p,
}
e.mu.Lock()
e.addressableEndpointState.Init(e)
e.addressableEndpointState.Init(e, stack.AddressableEndpointStateOptions{HiddenWhileDisabled: false})
e.igmp.init(e)
e.mu.Unlock()
@@ -276,6 +276,9 @@ func (e *endpoint) enableLocked() tcpip.Error {
return nil
}
// Must be called after Enabled has already been set.
e.addressableEndpointState.OnNetworkEndpointEnabledChanged()
// Create an endpoint to receive broadcast packets on this interface.
ep, err := e.addressableEndpointState.AddAndAcquirePermanentAddress(ipv4BroadcastAddr, stack.AddressProperties{PEB: stack.NeverPrimaryEndpoint})
if err != nil {
@@ -364,6 +367,9 @@ func (e *endpoint) disableLocked() {
if !e.setEnabled(false) {
panic("should have only done work to disable the endpoint if it was enabled")
}
// Must be called after Enabled has been set.
e.addressableEndpointState.OnNetworkEndpointEnabledChanged()
}
// emitMulticastEvent emits a multicast forwarding event using the provided
@@ -1277,7 +1283,7 @@ func (e *endpoint) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, p
e.mu.RLock()
defer e.mu.RUnlock()
ep, err := e.addressableEndpointState.AddAndAcquirePermanentAddress(addr, properties)
ep, err := e.addressableEndpointState.AddAndAcquireAddress(addr, properties, stack.Permanent)
if err == nil {
e.sendQueuedReports()
}
@@ -1306,6 +1312,13 @@ func (e *endpoint) SetDeprecated(addr tcpip.Address, deprecated bool) tcpip.Erro
return e.addressableEndpointState.SetDeprecated(addr, deprecated)
}
// SetLifetimes implements stack.AddressableEndpoint.
func (e *endpoint) SetLifetimes(addr tcpip.Address, lifetimes stack.AddressLifetimes) tcpip.Error {
e.mu.RLock()
defer e.mu.RUnlock()
return e.addressableEndpointState.SetLifetimes(addr, lifetimes)
}
// MainAddress implements stack.AddressableEndpoint.
func (e *endpoint) MainAddress() tcpip.AddressWithPrefix {
e.mu.RLock()
+77 -51
View File
@@ -411,7 +411,7 @@ func (e *endpoint) dupTentativeAddrDetected(addr tcpip.Address, holderLinkAddr t
case ip.NonceNotEqual:
// If the address is a SLAAC address, do not invalidate its SLAAC prefix as an
// attempt will be made to generate a new address for it.
if err := e.removePermanentEndpointLocked(addressEndpoint, false /* allowSLAACInvalidation */, &stack.DADDupAddrDetected{HolderLinkAddress: holderLinkAddr}); err != nil {
if err := e.removePermanentEndpointLocked(addressEndpoint, false /* allowSLAACInvalidation */, stack.AddressRemovalDADFailed, &stack.DADDupAddrDetected{HolderLinkAddress: holderLinkAddr}); err != nil {
return err
}
@@ -541,6 +541,40 @@ func (e *endpoint) Enable() tcpip.Error {
return nil
}
// Perform DAD on the all the unicast IPv6 endpoints that are in the permanent
// state.
//
// Addresses may have already completed DAD but in the time since the endpoint
// was last enabled, other devices may have acquired the same addresses.
var err tcpip.Error
e.mu.addressableEndpointState.ForEachEndpoint(func(addressEndpoint stack.AddressEndpoint) bool {
addr := addressEndpoint.AddressWithPrefix().Address
if !header.IsV6UnicastAddress(addr) {
return true
}
switch kind := addressEndpoint.GetKind(); kind {
case stack.Permanent:
addressEndpoint.SetKind(stack.PermanentTentative)
fallthrough
case stack.PermanentTentative:
err = e.mu.ndp.startDuplicateAddressDetection(addr, addressEndpoint)
return err == nil
case stack.Temporary, stack.PermanentExpired:
return true
default:
panic(fmt.Sprintf("address %s has unknown kind %d", addressEndpoint.AddressWithPrefix(), kind))
}
})
// It is important to enable after starting DAD on all the addresses so that
// if DAD is disabled, the Tentative state is not observed.
//
// Must be called after Enabled has been set.
e.mu.addressableEndpointState.OnNetworkEndpointEnabledChanged()
if err != nil {
return err
}
// Groups may have been joined when the endpoint was disabled, or the
// endpoint may have left groups from the perspective of MLD when the
// endpoint was disabled. Either way, we need to let routers know to
@@ -570,33 +604,6 @@ func (e *endpoint) Enable() tcpip.Error {
panic(fmt.Sprintf("e.joinGroupLocked(%s): %s", header.IPv6AllNodesMulticastAddress, err))
}
// Perform DAD on the all the unicast IPv6 endpoints that are in the permanent
// state.
//
// Addresses may have already completed DAD but in the time since the endpoint
// was last enabled, other devices may have acquired the same addresses.
var err tcpip.Error
e.mu.addressableEndpointState.ForEachEndpoint(func(addressEndpoint stack.AddressEndpoint) bool {
addr := addressEndpoint.AddressWithPrefix().Address
if !header.IsV6UnicastAddress(addr) {
return true
}
switch addressEndpoint.GetKind() {
case stack.Permanent:
addressEndpoint.SetKind(stack.PermanentTentative)
fallthrough
case stack.PermanentTentative:
err = e.mu.ndp.startDuplicateAddressDetection(addr, addressEndpoint)
return err == nil
default:
return true
}
})
if err != nil {
return err
}
// Do not auto-generate an IPv6 link-local address for loopback devices.
if e.protocol.options.AutoGenLinkLocal && !e.nic.IsLoopback() {
// The valid and preferred lifetime is infinite for the auto-generated
@@ -642,19 +649,6 @@ func (e *endpoint) disableLocked() {
}
e.mu.ndp.stopSolicitingRouters()
// Stop DAD for all the tentative unicast addresses.
e.mu.addressableEndpointState.ForEachEndpoint(func(addressEndpoint stack.AddressEndpoint) bool {
if addressEndpoint.GetKind() != stack.PermanentTentative {
return true
}
addr := addressEndpoint.AddressWithPrefix().Address
if header.IsV6UnicastAddress(addr) {
e.mu.ndp.stopDuplicateAddressDetection(addr, &stack.DADAborted{})
}
return true
})
e.mu.ndp.cleanupState()
// The endpoint may have already left the multicast group.
@@ -668,9 +662,27 @@ func (e *endpoint) disableLocked() {
// we are no longer interested in the group.
e.mu.mld.softLeaveAll()
// Stop DAD for all the tentative unicast addresses.
e.mu.addressableEndpointState.ForEachEndpoint(func(addressEndpoint stack.AddressEndpoint) bool {
addrWithPrefix := addressEndpoint.AddressWithPrefix()
switch kind := addressEndpoint.GetKind(); kind {
case stack.Permanent, stack.PermanentTentative:
if header.IsV6UnicastAddress(addrWithPrefix.Address) {
e.mu.ndp.stopDuplicateAddressDetection(addrWithPrefix.Address, &stack.DADAborted{})
}
case stack.Temporary, stack.PermanentExpired:
default:
panic(fmt.Sprintf("address %s has unknown address kind %d", addrWithPrefix, kind))
}
return true
})
if !e.setEnabled(false) {
panic("should have only done work to disable the endpoint if it was enabled")
}
// Must be called after Enabled has been set.
e.mu.addressableEndpointState.OnNetworkEndpointEnabledChanged()
}
// DefaultTTL is the default hop limit for this endpoint.
@@ -1763,7 +1775,16 @@ func (e *endpoint) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, p
// an empty address.
e.mu.Lock()
defer e.mu.Unlock()
return e.addAndAcquirePermanentAddressLocked(addr, properties)
// The dance of registering the dispatcher after adding the address makes it
// so that the tentative state is skipped if DAD is disabled.
addrDisp := properties.Disp
properties.Disp = nil
addressEndpoint, err := e.addAndAcquirePermanentAddressLocked(addr, properties)
if addrDisp != nil && err == nil {
addressEndpoint.RegisterDispatcher(addrDisp)
}
return addressEndpoint, err
}
// addAndAcquirePermanentAddressLocked is like AddAndAcquirePermanentAddress but
@@ -1774,7 +1795,7 @@ func (e *endpoint) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, p
//
// Precondition: e.mu must be write locked.
func (e *endpoint) addAndAcquirePermanentAddressLocked(addr tcpip.AddressWithPrefix, properties stack.AddressProperties) (stack.AddressEndpoint, tcpip.Error) {
addressEndpoint, err := e.mu.addressableEndpointState.AddAndAcquirePermanentAddress(addr, properties)
addressEndpoint, err := e.mu.addressableEndpointState.AddAndAcquireAddress(addr, properties, stack.PermanentTentative)
if err != nil {
return nil, err
}
@@ -1783,8 +1804,6 @@ func (e *endpoint) addAndAcquirePermanentAddressLocked(addr tcpip.AddressWithPre
return addressEndpoint, nil
}
addressEndpoint.SetKind(stack.PermanentTentative)
if e.Enabled() {
if err := e.mu.ndp.startDuplicateAddressDetection(addr.Address, addressEndpoint); err != nil {
return nil, err
@@ -1811,14 +1830,14 @@ func (e *endpoint) RemovePermanentAddress(addr tcpip.Address) tcpip.Error {
return &tcpip.ErrBadLocalAddress{}
}
return e.removePermanentEndpointLocked(addressEndpoint, true /* allowSLAACInvalidation */, &stack.DADAborted{})
return e.removePermanentEndpointLocked(addressEndpoint, true /* allowSLAACInvalidation */, stack.AddressRemovalManualAction, &stack.DADAborted{})
}
// removePermanentEndpointLocked is like removePermanentAddressLocked except
// it works with a stack.AddressEndpoint.
//
// Precondition: e.mu must be write locked.
func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEndpoint, allowSLAACInvalidation bool, dadResult stack.DADResult) tcpip.Error {
func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEndpoint, allowSLAACInvalidation bool, reason stack.AddressRemovalReason, dadResult stack.DADResult) tcpip.Error {
addr := addressEndpoint.AddressWithPrefix()
// If we are removing an address generated via SLAAC, cleanup
// its SLAAC resources and notify the integrator.
@@ -1830,18 +1849,18 @@ func (e *endpoint) removePermanentEndpointLocked(addressEndpoint stack.AddressEn
}
}
return e.removePermanentEndpointInnerLocked(addressEndpoint, dadResult)
return e.removePermanentEndpointInnerLocked(addressEndpoint, reason, dadResult)
}
// 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, dadResult stack.DADResult) tcpip.Error {
func (e *endpoint) removePermanentEndpointInnerLocked(addressEndpoint stack.AddressEndpoint, reason stack.AddressRemovalReason, dadResult stack.DADResult) tcpip.Error {
addr := addressEndpoint.AddressWithPrefix()
e.mu.ndp.stopDuplicateAddressDetection(addr.Address, dadResult)
if err := e.mu.addressableEndpointState.RemovePermanentEndpoint(addressEndpoint); err != nil {
if err := e.mu.addressableEndpointState.RemovePermanentEndpoint(addressEndpoint, reason); err != nil {
return err
}
@@ -1880,6 +1899,13 @@ func (e *endpoint) SetDeprecated(addr tcpip.Address, deprecated bool) tcpip.Erro
return e.mu.addressableEndpointState.SetDeprecated(addr, deprecated)
}
// SetLifetimes implements stack.AddressableEndpoint.
func (e *endpoint) SetLifetimes(addr tcpip.Address, lifetimes stack.AddressLifetimes) tcpip.Error {
e.mu.RLock()
defer e.mu.RUnlock()
return e.mu.addressableEndpointState.SetLifetimes(addr, lifetimes)
}
// MainAddress implements stack.AddressableEndpoint.
func (e *endpoint) MainAddress() tcpip.AddressWithPrefix {
e.mu.RLock()
@@ -2198,7 +2224,7 @@ func (p *protocol) NewEndpoint(nic stack.NetworkInterface, dispatcher stack.Tran
}
e.mu.Lock()
e.mu.addressableEndpointState.Init(e)
e.mu.addressableEndpointState.Init(e, stack.AddressableEndpointStateOptions{HiddenWhileDisabled: true})
e.mu.ndp.init(e, dadOptions)
e.mu.mld.init(e)
e.dad.mu.Lock()
+81 -25
View File
@@ -235,12 +235,15 @@ type NDPDispatcher interface {
//
// This function is not permitted to block indefinitely. It must not
// call functions on the stack itself.
OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix)
//
// If a non-nil AddressDispatcher is returned, events related to the address
// will be sent to the dispatcher.
OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) stack.AddressDispatcher
// OnAutoGenAddressDeprecated is called when an auto-generated address (SLAAC)
// is deprecated, but is still considered valid. Note, if an address is
// invalidated at the same ime it is deprecated, the deprecation event may not
// be received.
// invalidated at the same time it is deprecated, the deprecation event may
// not be received.
//
// This function is not permitted to block indefinitely. It must not
// call functions on the stack itself.
@@ -1089,6 +1092,12 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
t := now.Add(pl)
state.preferredUntil = &t
}
// The time at which an address is invalidated is exposed as a property of the
// address.
if vl < header.NDPInfiniteLifetime {
t := now.Add(vl)
state.validUntil = &t
}
if !ndp.generateSLAACAddr(prefix, &state) {
// We were unable to generate an address for the prefix, we do not nothing
@@ -1105,8 +1114,6 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
if vl < header.NDPInfiniteLifetime {
state.invalidationJob.Schedule(vl)
t := now.Add(vl)
state.validUntil = &t
}
// If the address is assigned (DAD resolved), generate a temporary address.
@@ -1122,7 +1129,7 @@ func (ndp *ndpState) doSLAAC(prefix tcpip.Subnet, pl, vl time.Duration) {
// addAndAcquireSLAACAddr adds a SLAAC address to the IPv6 endpoint.
//
// The IPv6 endpoint that ndp belongs to MUST be locked.
func (ndp *ndpState) addAndAcquireSLAACAddr(addr tcpip.AddressWithPrefix, temporary bool, deprecated bool) stack.AddressEndpoint {
func (ndp *ndpState) addAndAcquireSLAACAddr(addr tcpip.AddressWithPrefix, temporary bool, lifetimes stack.AddressLifetimes) stack.AddressEndpoint {
// Inform the integrator that we have a new SLAAC address.
ndpDisp := ndp.ep.protocol.options.NDPDisp
if ndpDisp == nil {
@@ -1132,14 +1139,16 @@ func (ndp *ndpState) addAndAcquireSLAACAddr(addr tcpip.AddressWithPrefix, tempor
addressEndpoint, err := ndp.ep.addAndAcquirePermanentAddressLocked(addr, stack.AddressProperties{
PEB: stack.FirstPrimaryEndpoint,
ConfigType: stack.AddressConfigSlaac,
Deprecated: deprecated,
Lifetimes: lifetimes,
Temporary: temporary,
})
if err != nil {
panic(fmt.Sprintf("ndp: error when adding SLAAC address %+v: %s", addr, err))
}
ndpDisp.OnAutoGenAddress(ndp.ep.nic.ID(), addr)
if disp := ndpDisp.OnAutoGenAddress(ndp.ep.nic.ID(), addr); disp != nil {
addressEndpoint.RegisterDispatcher(disp)
}
return addressEndpoint
}
@@ -1217,7 +1226,23 @@ func (ndp *ndpState) generateSLAACAddr(prefix tcpip.Subnet, state *slaacPrefixSt
}
deprecated := state.preferredUntil != nil && !state.preferredUntil.After(ndp.ep.protocol.stack.Clock().NowMonotonic())
if addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, false /* temporary */, deprecated); addressEndpoint != nil {
var preferredUntil tcpip.MonotonicTime
if !deprecated {
if state.preferredUntil != nil {
preferredUntil = *state.preferredUntil
} else {
preferredUntil = tcpip.MonotonicTimeInfinite()
}
}
validUntil := tcpip.MonotonicTimeInfinite()
if state.validUntil != nil {
validUntil = *state.validUntil
}
if addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, false /* temporary */, stack.AddressLifetimes{
Deprecated: deprecated,
PreferredUntil: preferredUntil,
ValidUntil: validUntil,
}); addressEndpoint != nil {
state.stableAddr.addressEndpoint = addressEndpoint
state.generationAttempts++
return true
@@ -1329,7 +1354,11 @@ func (ndp *ndpState) generateTempSLAACAddr(prefix tcpip.Subnet, prefixState *sla
// As per RFC RFC 4941 section 3.3 step 5, we MUST NOT create a temporary
// address with a zero preferred lifetime. The checks above ensure this
// so we know the address is not deprecated.
addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, true /* temporary */, false /* deprecated */)
addressEndpoint := ndp.addAndAcquireSLAACAddr(generatedAddr, true /* temporary */, stack.AddressLifetimes{
Deprecated: false,
PreferredUntil: now.Add(pl),
ValidUntil: now.Add(vl),
})
if addressEndpoint == nil {
return false
}
@@ -1417,14 +1446,6 @@ func (ndp *ndpState) regenerateTempSLAACAddr(prefix tcpip.Subnet, resetGenAttemp
//
// The IPv6 endpoint that ndp belongs to MUST be locked.
func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixState *slaacPrefixState, pl, vl time.Duration) {
// If the preferred lifetime is zero, then the prefix should be deprecated.
deprecated := pl == 0
if deprecated {
ndp.deprecateSLAACAddress(prefixState.stableAddr.addressEndpoint)
} else {
prefixState.stableAddr.addressEndpoint.SetDeprecated(false)
}
// If prefix was preferred for some finite lifetime before, cancel the
// deprecation job so it can be reset.
prefixState.deprecationJob.Cancel()
@@ -1432,6 +1453,7 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
now := ndp.ep.protocol.stack.Clock().NowMonotonic()
// Schedule the deprecation job if prefix has a finite preferred lifetime.
deprecated := pl == 0
if pl < header.NDPInfiniteLifetime {
if !deprecated {
prefixState.deprecationJob.Schedule(pl)
@@ -1484,6 +1506,32 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
}
}
// If the preferred lifetime is zero, then the prefix should be deprecated.
{
var preferredUntil tcpip.MonotonicTime
if !deprecated {
if prefixState.preferredUntil == nil {
preferredUntil = tcpip.MonotonicTimeInfinite()
} else {
preferredUntil = *prefixState.preferredUntil
}
}
validUntil := tcpip.MonotonicTimeInfinite()
if prefixState.validUntil != nil {
validUntil = *prefixState.validUntil
}
if addressEndpoint := prefixState.stableAddr.addressEndpoint; !addressEndpoint.Deprecated() && deprecated {
if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
ndpDisp.OnAutoGenAddressDeprecated(ndp.ep.nic.ID(), addressEndpoint.AddressWithPrefix())
}
}
prefixState.stableAddr.addressEndpoint.SetLifetimes(stack.AddressLifetimes{
Deprecated: deprecated,
PreferredUntil: preferredUntil,
ValidUntil: validUntil,
})
}
// If DAD is not yet complete on the stable address, there is no need to do
// work with temporary addresses.
if prefixState.stableAddr.addressEndpoint.GetKind() != stack.Permanent {
@@ -1528,14 +1576,22 @@ func (ndp *ndpState) refreshSLAACPrefixLifetimes(prefix tcpip.Subnet, prefixStat
// Otherwise, schedule the deprecation job again.
newPreferredLifetime := preferredUntil.Sub(now)
tempAddrState.deprecationJob.Cancel()
if newPreferredLifetime <= 0 {
ndp.deprecateSLAACAddress(tempAddrState.addressEndpoint)
} else {
tempAddrState.addressEndpoint.SetDeprecated(false)
deprecated := newPreferredLifetime <= 0
if !deprecated {
tempAddrState.deprecationJob.Schedule(newPreferredLifetime)
}
if addressEndpoint := tempAddrState.addressEndpoint; !addressEndpoint.Deprecated() && deprecated {
if ndpDisp := ndp.ep.protocol.options.NDPDisp; ndpDisp != nil {
ndpDisp.OnAutoGenAddressDeprecated(ndp.ep.nic.ID(), addressEndpoint.AddressWithPrefix())
}
}
tempAddrState.addressEndpoint.SetLifetimes(stack.AddressLifetimes{
Deprecated: deprecated,
ValidUntil: validUntil,
PreferredUntil: preferredUntil,
})
tempAddrState.regenJob.Cancel()
if tempAddrState.regenerated {
} else {
@@ -1597,7 +1653,7 @@ func (ndp *ndpState) invalidateSLAACPrefix(prefix tcpip.Subnet, state slaacPrefi
ndpDisp.OnAutoGenAddressInvalidated(ndp.ep.nic.ID(), addressEndpoint.AddressWithPrefix())
}
if err := ndp.ep.removePermanentEndpointInnerLocked(addressEndpoint, &stack.DADAborted{}); err != nil {
if err := ndp.ep.removePermanentEndpointInnerLocked(addressEndpoint, stack.AddressRemovalInvalidated, &stack.DADAborted{}); err != nil {
panic(fmt.Sprintf("ndp: error removing stable SLAAC address %s: %s", addressEndpoint.AddressWithPrefix(), err))
}
}
@@ -1656,7 +1712,7 @@ func (ndp *ndpState) cleanupSLAACPrefixResources(prefix tcpip.Subnet, state slaa
func (ndp *ndpState) invalidateTempSLAACAddr(tempAddrs map[tcpip.Address]tempSLAACAddrState, tempAddr tcpip.Address, tempAddrState tempSLAACAddrState) {
ndp.cleanupTempSLAACAddrResourcesAndNotifyInner(tempAddrs, tempAddr, tempAddrState)
if err := ndp.ep.removePermanentEndpointInnerLocked(tempAddrState.addressEndpoint, &stack.DADAborted{}); err != nil {
if err := ndp.ep.removePermanentEndpointInnerLocked(tempAddrState.addressEndpoint, stack.AddressRemovalInvalidated, &stack.DADAborted{}); err != nil {
panic(fmt.Sprintf("error removing temporary SLAAC address %s: %s", tempAddrState.addressEndpoint.AddressWithPrefix(), err))
}
}
+2 -1
View File
@@ -57,7 +57,8 @@ func (*testNDPDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) {
func (*testNDPDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) {
}
func (*testNDPDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) {
func (*testNDPDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) stack.AddressDispatcher {
return nil
}
func (*testNDPDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {
+196 -43
View File
@@ -21,11 +21,18 @@ import (
"gvisor.dev/gvisor/pkg/tcpip"
)
func (lifetimes *AddressLifetimes) sanitize() {
if lifetimes.Deprecated {
lifetimes.PreferredUntil = tcpip.MonotonicTime{}
}
}
var _ AddressableEndpoint = (*AddressableEndpointState)(nil)
// AddressableEndpointState is an implementation of an AddressableEndpoint.
type AddressableEndpointState struct {
networkEndpoint NetworkEndpoint
options AddressableEndpointStateOptions
// Lock ordering (from outer to inner lock ordering):
//
@@ -38,17 +45,42 @@ type AddressableEndpointState struct {
primary []*addressState
}
// AddressableEndpointStateOptions contains options used to configure an
// AddressableEndpointState.
type AddressableEndpointStateOptions struct {
// HiddenWhileDisabled determines whether addresses should be returned to
// callers while the NetworkEndpoint this AddressableEndpointState belongs
// to is disabled.
HiddenWhileDisabled bool
}
// Init initializes the AddressableEndpointState with networkEndpoint.
//
// Must be called before calling any other function on m.
func (a *AddressableEndpointState) Init(networkEndpoint NetworkEndpoint) {
func (a *AddressableEndpointState) Init(networkEndpoint NetworkEndpoint, options AddressableEndpointStateOptions) {
a.networkEndpoint = networkEndpoint
a.options = options
a.mu.Lock()
defer a.mu.Unlock()
a.endpoints = make(map[tcpip.Address]*addressState)
}
// OnNetworkEndpointEnabledChanged must be called every time the
// NetworkEndpoint this AddressableEndpointState belongs to is enabled or
// disabled so that any AddressDispatchers can be notified of the NIC enabled
// change.
func (a *AddressableEndpointState) OnNetworkEndpointEnabledChanged() {
a.mu.RLock()
defer a.mu.RUnlock()
for _, ep := range a.endpoints {
ep.mu.Lock()
ep.notifyChangedLocked()
ep.mu.Unlock()
}
}
// GetAddress returns the AddressEndpoint for the passed address.
//
// GetAddress does not increment the address's reference count or check if the
@@ -118,27 +150,7 @@ func (a *AddressableEndpointState) releaseAddressStateLocked(addrState *addressS
// AddAndAcquirePermanentAddress implements AddressableEndpoint.
func (a *AddressableEndpointState) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, properties AddressProperties) (AddressEndpoint, tcpip.Error) {
a.mu.Lock()
defer a.mu.Unlock()
ep, err := a.addAndAcquireAddressLocked(addr, properties, true /* permanent */)
// From https://golang.org/doc/faq#nil_error:
//
// Under the covers, interfaces are implemented as two elements, a type T and
// a value V.
//
// An interface value is nil only if the V and T are both unset, (T=nil, V is
// not set), In particular, a nil interface will always hold a nil type. If we
// store a nil pointer of type *int inside an interface value, the inner type
// will be *int regardless of the value of the pointer: (T=*int, V=nil). Such
// an interface value will therefore be non-nil even when the pointer value V
// inside is nil.
//
// Since addAndAcquireAddressLocked returns a nil value with a non-nil type,
// we need to explicitly return nil below if ep is (a typed) nil.
if ep == nil {
return nil, err
}
return ep, err
return a.AddAndAcquireAddress(addr, properties, Permanent)
}
// AddAndAcquireTemporaryAddress adds a temporary address.
@@ -147,9 +159,16 @@ func (a *AddressableEndpointState) AddAndAcquirePermanentAddress(addr tcpip.Addr
//
// The temporary address's endpoint is acquired and returned.
func (a *AddressableEndpointState) AddAndAcquireTemporaryAddress(addr tcpip.AddressWithPrefix, peb PrimaryEndpointBehavior) (AddressEndpoint, tcpip.Error) {
return a.AddAndAcquireAddress(addr, AddressProperties{PEB: peb}, Temporary)
}
// AddAndAcquireAddress adds an address with the specified kind.
//
// Returns *tcpip.ErrDuplicateAddress if the address exists.
func (a *AddressableEndpointState) AddAndAcquireAddress(addr tcpip.AddressWithPrefix, properties AddressProperties, kind AddressKind) (AddressEndpoint, tcpip.Error) {
a.mu.Lock()
defer a.mu.Unlock()
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: peb}, false /* permanent */)
ep, err := a.addAndAcquireAddressLocked(addr, properties, kind)
// From https://golang.org/doc/faq#nil_error:
//
// Under the covers, interfaces are implemented as two elements, a type T and
@@ -180,7 +199,17 @@ func (a *AddressableEndpointState) AddAndAcquireTemporaryAddress(addr tcpip.Addr
// returned, regardless the kind of address that is being added.
//
// +checklocks:a.mu
func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.AddressWithPrefix, properties AddressProperties, permanent bool) (*addressState, tcpip.Error) {
func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.AddressWithPrefix, properties AddressProperties, kind AddressKind) (*addressState, tcpip.Error) {
var permanent bool
switch kind {
case PermanentExpired:
panic(fmt.Sprintf("cannot add address %s in PermanentExpired state", addr))
case Permanent, PermanentTentative:
permanent = true
case Temporary:
default:
panic(fmt.Sprintf("unknown address kind: %d", kind))
}
// attemptAddToPrimary is false when the address is already in the primary
// address list.
attemptAddToPrimary := true
@@ -241,6 +270,7 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
// We never promote an address to temporary - it can only be added as such.
// If we are actually adding a permanent address, it is promoted below.
addrState.kind = Temporary
addrState.disp = properties.Disp
}
// At this point we have an address we are either promoting from an expired or
@@ -258,12 +288,14 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
// Primary addresses are biased by 1.
addrState.refs++
addrState.kind = Permanent
addrState.kind = kind
}
// Acquire the address before returning it.
addrState.refs++
addrState.deprecated = properties.Deprecated
addrState.configType = properties.ConfigType
lifetimes := properties.Lifetimes
lifetimes.sanitize()
addrState.lifetimes = lifetimes
if attemptAddToPrimary {
switch properties.PEB {
@@ -289,6 +321,7 @@ func (a *AddressableEndpointState) addAndAcquireAddressLocked(addr tcpip.Address
}
}
addrState.notifyChangedLocked()
return addrState, nil
}
@@ -309,12 +342,12 @@ func (a *AddressableEndpointState) removePermanentAddressLocked(addr tcpip.Addre
return &tcpip.ErrBadLocalAddress{}
}
return a.removePermanentEndpointLocked(addrState)
return a.removePermanentEndpointLocked(addrState, AddressRemovalManualAction)
}
// RemovePermanentEndpoint removes the passed endpoint if it is associated with
// a and permanent.
func (a *AddressableEndpointState) RemovePermanentEndpoint(ep AddressEndpoint) tcpip.Error {
func (a *AddressableEndpointState) RemovePermanentEndpoint(ep AddressEndpoint, reason AddressRemovalReason) tcpip.Error {
addrState, ok := ep.(*addressState)
if !ok || addrState.addressableEndpointState != a {
return &tcpip.ErrInvalidEndpointState{}
@@ -322,19 +355,19 @@ func (a *AddressableEndpointState) RemovePermanentEndpoint(ep AddressEndpoint) t
a.mu.Lock()
defer a.mu.Unlock()
return a.removePermanentEndpointLocked(addrState)
return a.removePermanentEndpointLocked(addrState, reason)
}
// removePermanentAddressLocked is like RemovePermanentAddress but with locking
// requirements.
//
// +checklocks:a.mu
func (a *AddressableEndpointState) removePermanentEndpointLocked(addrState *addressState) tcpip.Error {
func (a *AddressableEndpointState) removePermanentEndpointLocked(addrState *addressState, reason AddressRemovalReason) tcpip.Error {
if !addrState.GetKind().IsPermanent() {
return &tcpip.ErrBadLocalAddress{}
}
addrState.SetKind(PermanentExpired)
addrState.remove(reason)
a.decAddressRefLocked(addrState)
return nil
}
@@ -386,6 +419,19 @@ func (a *AddressableEndpointState) SetDeprecated(addr tcpip.Address, deprecated
return nil
}
// SetLifetimes implements stack.AddressableEndpoint.
func (a *AddressableEndpointState) SetLifetimes(addr tcpip.Address, lifetimes AddressLifetimes) tcpip.Error {
a.mu.RLock()
defer a.mu.RUnlock()
addrState, ok := a.endpoints[addr]
if !ok {
return &tcpip.ErrBadLocalAddress{}
}
addrState.SetLifetimes(lifetimes)
return nil
}
// MainAddress implements AddressableEndpoint.
func (a *AddressableEndpointState) MainAddress() tcpip.AddressWithPrefix {
a.mu.RLock()
@@ -394,7 +440,7 @@ func (a *AddressableEndpointState) MainAddress() tcpip.AddressWithPrefix {
ep := a.acquirePrimaryAddressRLocked(func(ep *addressState) bool {
switch kind := ep.GetKind(); kind {
case Permanent:
return true
return a.networkEndpoint.Enabled() || !a.options.HiddenWhileDisabled
case PermanentTentative, PermanentExpired, Temporary:
return false
default:
@@ -519,7 +565,7 @@ func (a *AddressableEndpointState) AcquireAssignedAddressOrMatching(localAddr tc
// Proceed to add a new temporary endpoint.
addr := localAddr.WithPrefix()
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: tempPEB}, false /* permanent */)
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: tempPEB}, Temporary)
if err != nil {
// addAndAcquireAddressLocked only returns an error if the address is
// already assigned but we just checked above if the address exists so we
@@ -588,6 +634,9 @@ func (a *AddressableEndpointState) PrimaryAddresses() []tcpip.AddressWithPrefix
defer a.mu.RUnlock()
var addrs []tcpip.AddressWithPrefix
if a.options.HiddenWhileDisabled && !a.networkEndpoint.Enabled() {
return addrs
}
for _, ep := range a.primary {
switch kind := ep.GetKind(); kind {
// Don't include tentative, expired or temporary endpoints
@@ -631,7 +680,7 @@ func (a *AddressableEndpointState) Cleanup() {
for _, ep := range a.endpoints {
// removePermanentEndpointLocked returns *tcpip.ErrBadLocalAddress if ep is
// not a permanent address.
switch err := a.removePermanentEndpointLocked(ep); err.(type) {
switch err := a.removePermanentEndpointLocked(ep, AddressRemovalInterfaceRemoved); err.(type) {
case nil, *tcpip.ErrBadLocalAddress:
default:
panic(fmt.Sprintf("unexpected error from removePermanentEndpointLocked(%s): %s", ep.addr, err))
@@ -659,8 +708,19 @@ type addressState struct {
kind AddressKind
// checklocks:mu
configType AddressConfigType
// lifetimes holds this address' lifetimes.
//
// Invariant: if lifetimes.deprecated is true, then lifetimes.PreferredUntil
// must be the zero value. Note that the converse does not need to be
// upheld!
//
// checklocks:mu
deprecated bool
lifetimes AddressLifetimes
// The enclosing mutex must be write-locked before calling methods on the
// dispatcher.
//
// checklocks:mu
disp AddressDispatcher
}
// AddressWithPrefix implements AddressEndpoint.
@@ -684,22 +744,45 @@ func (a *addressState) GetKind() AddressKind {
func (a *addressState) SetKind(kind AddressKind) {
a.mu.Lock()
defer a.mu.Unlock()
prevKind := a.kind
a.kind = kind
if kind == PermanentExpired {
a.notifyRemovedLocked(AddressRemovalManualAction)
} else if prevKind != kind && a.addressableEndpointState.networkEndpoint.Enabled() {
a.notifyChangedLocked()
}
}
// notifyRemovedLocked notifies integrators of address removal.
//
// +checklocks:a.mu
func (a *addressState) notifyRemovedLocked(reason AddressRemovalReason) {
if disp := a.disp; disp != nil {
a.disp.OnRemoved(reason)
a.disp = nil
}
}
func (a *addressState) remove(reason AddressRemovalReason) {
a.mu.Lock()
defer a.mu.Unlock()
a.kind = PermanentExpired
a.notifyRemovedLocked(reason)
}
// IsAssigned implements AddressEndpoint.
func (a *addressState) IsAssigned(allowExpired bool) bool {
if !a.addressableEndpointState.networkEndpoint.Enabled() {
return false
}
switch a.GetKind() {
switch kind := a.GetKind(); kind {
case PermanentTentative:
return false
case PermanentExpired:
return allowExpired
default:
case Permanent, Temporary:
return true
default:
panic(fmt.Sprintf("address %s has unknown kind %d", a.AddressWithPrefix(), kind))
}
}
@@ -742,21 +825,91 @@ func (a *addressState) ConfigType() AddressConfigType {
return a.configType
}
// notifyChangedLocked notifies integrators of address property changes.
//
// +checklocks:a.mu
func (a *addressState) notifyChangedLocked() {
if a.disp == nil {
return
}
state := AddressDisabled
if a.addressableEndpointState.networkEndpoint.Enabled() {
switch a.kind {
case Permanent:
state = AddressAssigned
case PermanentTentative:
state = AddressTentative
case Temporary, PermanentExpired:
return
default:
panic(fmt.Sprintf("unrecognized address kind = %d", a.kind))
}
}
a.disp.OnChanged(a.lifetimes, state)
}
// SetDeprecated implements AddressEndpoint.
func (a *addressState) SetDeprecated(d bool) {
a.mu.Lock()
defer a.mu.Unlock()
a.deprecated = d
var changed bool
if a.lifetimes.Deprecated != d {
a.lifetimes.Deprecated = d
changed = true
}
if d {
a.lifetimes.PreferredUntil = tcpip.MonotonicTime{}
}
if changed {
a.notifyChangedLocked()
}
}
// Deprecated implements AddressEndpoint.
func (a *addressState) Deprecated() bool {
a.mu.RLock()
defer a.mu.RUnlock()
return a.deprecated
return a.lifetimes.Deprecated
}
// SetLifetimes implements AddressEndpoint.
func (a *addressState) SetLifetimes(lifetimes AddressLifetimes) {
a.mu.Lock()
defer a.mu.Unlock()
lifetimes.sanitize()
var changed bool
if a.lifetimes != lifetimes {
changed = true
}
a.lifetimes = lifetimes
if changed {
a.notifyChangedLocked()
}
}
// Lifetimes implements AddressEndpoint.
func (a *addressState) Lifetimes() AddressLifetimes {
a.mu.RLock()
defer a.mu.RUnlock()
return a.lifetimes
}
// Temporary implements AddressEndpoint.
func (a *addressState) Temporary() bool {
return a.temporary
}
// RegisterDispatcher implements AddressEndpoint.
func (a *addressState) RegisterDispatcher(disp AddressDispatcher) {
a.mu.Lock()
defer a.mu.Unlock()
if disp != nil {
a.disp = disp
a.notifyChangedLocked()
}
}
@@ -30,7 +30,7 @@ func TestAddressableEndpointStateCleanup(t *testing.T) {
}
var s stack.AddressableEndpointState
s.Init(&ep)
s.Init(&ep, stack.AddressableEndpointStateOptions{HiddenWhileDisabled: false})
addr := tcpip.AddressWithPrefix{
Address: "\x01",
+1 -1
View File
@@ -195,7 +195,7 @@ func (f *fwdTestNetworkProtocol) NewEndpoint(nic NetworkInterface, dispatcher Tr
proto: f,
dispatcher: dispatcher,
}
e.AddressableEndpointState.Init(e)
e.AddressableEndpointState.Init(e, AddressableEndpointStateOptions{HiddenWhileDisabled: false})
return e
}
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -597,14 +597,14 @@ func (n *nic) removeAddress(addr tcpip.Address) tcpip.Error {
return &tcpip.ErrBadLocalAddress{}
}
func (n *nic) setAddressDeprecated(addr tcpip.Address, deprecated bool) tcpip.Error {
func (n *nic) setAddressLifetimes(addr tcpip.Address, lifetimes AddressLifetimes) tcpip.Error {
for _, ep := range n.networkEndpoints {
ep, ok := ep.(AddressableEndpoint)
if !ok {
continue
}
switch err := ep.SetDeprecated(addr, deprecated); err.(type) {
switch err := ep.SetLifetimes(addr, lifetimes); err.(type) {
case *tcpip.ErrBadLocalAddress:
continue
default:
+1 -1
View File
@@ -134,7 +134,7 @@ func (p *testIPv6Protocol) NewEndpoint(nic NetworkInterface, _ TransportDispatch
nic: nic,
protocol: p,
}
e.AddressableEndpointState.Init(e)
e.AddressableEndpointState.Init(e, AddressableEndpointStateOptions{HiddenWhileDisabled: false})
return e
}
+145 -3
View File
@@ -366,17 +366,143 @@ const (
AddressConfigSlaac
)
// AddressLifetimes encodes an address' preferred and valid lifetimes, as well
// as if the address is deprecated.
type AddressLifetimes struct {
// Deprecated is whether the address is deprecated.
Deprecated bool
// PreferredUntil is the time at which the address will be deprecated.
//
// Note that for certain addresses, deprecating the address at the
// PreferredUntil time is not handled as a scheduled job by the stack, but
// is information provided by the owner as an indication of when it will
// deprecate the address.
//
// PreferredUntil should be ignored if Deprecated is true. If Deprecated
// is false, and PreferredUntil is the zero value, no information about
// the preferred lifetime can be inferred.
PreferredUntil tcpip.MonotonicTime
// ValidUntil is the time at which the address will be invalidated.
//
// Note that for certain addresses, invalidating the address at the
// ValidUntil time is not handled as a scheduled job by the stack, but
// is information provided by the owner as an indication of when it will
// invalidate the address.
//
// If ValidUntil is the zero value, no information about the valid lifetime
// can be inferred.
ValidUntil tcpip.MonotonicTime
}
// AddressProperties contains additional properties that can be configured when
// adding an address.
type AddressProperties struct {
PEB PrimaryEndpointBehavior
ConfigType AddressConfigType
Deprecated bool
// Lifetimes encodes the address' lifetimes.
//
// Lifetimes.PreferredUntil and Lifetimes.ValidUntil are informational, i.e.
// the stack will not deprecated nor invalidate the address upon reaching
// these timestamps.
//
// If Lifetimes.Deprecated is true, the address will be added as deprecated.
Lifetimes AddressLifetimes
// Temporary is as defined in RFC 4941, but applies not only to addresses
// added via SLAAC, e.g. DHCPv6 can also add temporary addresses. Temporary
// addresses are short-lived and are not to be valid (or preferred)
// forever; hence the term temporary.
Temporary bool
Disp AddressDispatcher
}
// AddressAssignmentState is an address' assignment state.
type AddressAssignmentState int
const (
_ AddressAssignmentState = iota
// AddressDisabled indicates the NIC the address is assigned to is disabled.
AddressDisabled
// AddressTentative indicates an address is yet to pass DAD (IPv4 addresses
// are never tentative).
AddressTentative
// AddressAssigned indicates an address is assigned.
AddressAssigned
)
func (state AddressAssignmentState) String() string {
switch state {
case AddressDisabled:
return "Disabled"
case AddressTentative:
return "Tentative"
case AddressAssigned:
return "Assigned"
default:
panic(fmt.Sprintf("unknown address assignment state: %d", state))
}
}
// AddressRemovalReason is the reason an address was removed.
type AddressRemovalReason int
const (
_ AddressRemovalReason = iota
// AddressRemovalManualAction indicates the address was removed explicitly
// using the stack API.
AddressRemovalManualAction
// AddressRemovalInterfaceRemoved indicates the address was removed because
// the NIC it is assigned to was removed.
AddressRemovalInterfaceRemoved
// AddressRemovalDADFailed indicates the address was removed because DAD
// failed.
AddressRemovalDADFailed
// AddressRemovalInvalidated indicates the address was removed because it
// was invalidated.
AddressRemovalInvalidated
)
func (reason AddressRemovalReason) String() string {
switch reason {
case AddressRemovalManualAction:
return "ManualAction"
case AddressRemovalInterfaceRemoved:
return "InterfaceRemoved"
case AddressRemovalDADFailed:
return "DADFailed"
case AddressRemovalInvalidated:
return "Invalidated"
default:
panic(fmt.Sprintf("unknown address removal reason: %d", reason))
}
}
// AddressDispatcher is the interface integrators can implement to receive
// address-related events.
type AddressDispatcher interface {
// OnChanged is called with an address' properties when they change.
//
// OnChanged is called once when the address is added with the initial state,
// and every time a property changes.
//
// The PreferredUntil and ValidUntil fields in AddressLifetimes must be
// considered informational, i.e. one must not consider an address to be
// deprecated/invalid even if the monotonic clock timestamp is past these
// deadlines. The Deprecated field indicates whether an address is
// preferred or not; and OnRemoved will be called when an address is
// removed due to invalidation.
OnChanged(AddressLifetimes, AddressAssignmentState)
// OnRemoved is called when an address is removed with the removal reason.
OnRemoved(AddressRemovalReason)
}
// AssignableAddressEndpoint is a reference counted address endpoint that may be
@@ -422,8 +548,23 @@ type AddressEndpoint interface {
// SetDeprecated sets this endpoint's deprecated status.
SetDeprecated(bool)
// Lifetimes returns this endpoint's lifetimes.
Lifetimes() AddressLifetimes
// SetLifetimes sets this endpoint's lifetimes.
//
// Note that setting preferred-until and valid-until times do not result in
// deprecation/invalidation jobs to be scheduled by the stack.
SetLifetimes(AddressLifetimes)
// Temporary returns whether or not this endpoint is temporary.
Temporary() bool
// RegisterDispatcher registers an address dispatcher.
//
// OnChanged will be called immediately on the provided address dispatcher
// with this endpoint's current state.
RegisterDispatcher(AddressDispatcher)
}
// AddressKind is the kind of an address.
@@ -497,11 +638,12 @@ type AddressableEndpoint interface {
// permanent address.
RemovePermanentAddress(addr tcpip.Address) tcpip.Error
// SetDeprecated sets whether the address should be deprecated or not.
// SetLifetimes sets an address' lifetimes (strictly informational) and
// whether it should be deprecated or preferred.
//
// Returns *tcpip.ErrBadLocalAddress if the endpoint does not have the passed
// address.
SetDeprecated(addr tcpip.Address, deprecated bool) tcpip.Error
SetLifetimes(addr tcpip.Address, lifetimes AddressLifetimes) tcpip.Error
// MainAddress returns the endpoint's primary permanent address.
MainAddress() tcpip.AddressWithPrefix
+4 -3
View File
@@ -1096,13 +1096,14 @@ func (s *Stack) RemoveAddress(id tcpip.NICID, addr tcpip.Address) tcpip.Error {
return &tcpip.ErrUnknownNICID{}
}
// SetAddressDeprecated sets an address to be deprecated or preferred.
func (s *Stack) SetAddressDeprecated(id tcpip.NICID, addr tcpip.Address, deprecated bool) tcpip.Error {
// SetAddressLifetimes sets informational preferred and valid lifetimes, and
// whether the address should be preferred or deprecated.
func (s *Stack) SetAddressLifetimes(id tcpip.NICID, addr tcpip.Address, lifetimes AddressLifetimes) tcpip.Error {
s.mu.RLock()
defer s.mu.RUnlock()
if nic, ok := s.nics[id]; ok {
return nic.setAddressDeprecated(addr, deprecated)
return nic.setAddressLifetimes(addr, lifetimes)
}
return &tcpip.ErrUnknownNICID{}
+120 -12
View File
@@ -276,7 +276,7 @@ func (f *fakeNetworkProtocol) NewEndpoint(nic stack.NetworkInterface, dispatcher
proto: f,
dispatcher: dispatcher,
}
e.AddressableEndpointState.Init(e)
e.AddressableEndpointState.Init(e, stack.AddressableEndpointStateOptions{HiddenWhileDisabled: false})
return e
}
@@ -424,6 +424,117 @@ func containsAddr(list []tcpip.ProtocolAddress, item tcpip.ProtocolAddress) bool
return false
}
type addressChangedEvent struct {
lifetimes stack.AddressLifetimes
state stack.AddressAssignmentState
}
// An implementation of AddressDispatcher which forwards data from callbacks
// to channels to be asserted against in tests.
type addressDispatcher struct {
changedCh chan addressChangedEvent
removedCh chan stack.AddressRemovalReason
nicid tcpip.NICID
addr tcpip.AddressWithPrefix
lifetimes stack.AddressLifetimes
state stack.AddressAssignmentState
}
var _ stack.AddressDispatcher = (*addressDispatcher)(nil)
// OnChanged implements stack.AddressDispatcher.
func (ad *addressDispatcher) OnChanged(lifetimes stack.AddressLifetimes, state stack.AddressAssignmentState) {
if ad.changedCh != nil {
ad.changedCh <- addressChangedEvent{
lifetimes: lifetimes,
state: state,
}
}
}
// OnRemoved implements stack.AddressDispatcher.
func (ad *addressDispatcher) OnRemoved(reason stack.AddressRemovalReason) {
if ad.removedCh != nil {
ad.removedCh <- reason
}
}
func (ad *addressDispatcher) disable() {
ad.changedCh = nil
ad.removedCh = nil
}
func (ad *addressDispatcher) expectNoEvent() error {
select {
case e := <-ad.changedCh:
return fmt.Errorf("dispatcher for nic=%d addr=%s unexpectedly received changed event: %#v", ad.nicid, ad.addr, e)
case e := <-ad.removedCh:
return fmt.Errorf("dispatcher for nic=%d addr=%s unexpectedly received removed event: %#v", ad.nicid, ad.addr, e)
default:
return nil
}
}
func (ad *addressDispatcher) expectChanged(lifetimes stack.AddressLifetimes, state stack.AddressAssignmentState) error {
select {
case e := <-ad.changedCh:
ad.lifetimes = e.lifetimes
ad.state = e.state
if diff := cmp.Diff(e, addressChangedEvent{
lifetimes: lifetimes,
state: state,
}, cmp.AllowUnexported(e, tcpip.MonotonicTime{})); diff != "" {
return fmt.Errorf("dispatcher for nic=%d addr=%s address changed event mismatch (-got +want):\n%s", ad.nicid, ad.addr, diff)
}
default:
return fmt.Errorf("dispatcher for nic=%d addr=%s address changed event not immediately ready", ad.nicid, ad.addr)
}
return nil
}
func (ad *addressDispatcher) expectDeprecated() error {
return ad.expectChanged(stack.AddressLifetimes{
Deprecated: true,
ValidUntil: ad.lifetimes.ValidUntil,
}, ad.state)
}
func (ad *addressDispatcher) expectValidUntilChanged(validUntil tcpip.MonotonicTime) error {
return ad.expectChanged(stack.AddressLifetimes{
Deprecated: ad.lifetimes.Deprecated,
PreferredUntil: ad.lifetimes.PreferredUntil,
ValidUntil: validUntil,
}, ad.state)
}
func (ad *addressDispatcher) expectLifetimesChanged(lifetimes stack.AddressLifetimes) error {
return ad.expectChanged(lifetimes, ad.state)
}
func (ad *addressDispatcher) expectStateChanged(state stack.AddressAssignmentState) error {
return ad.expectChanged(ad.lifetimes, state)
}
func (ad *addressDispatcher) expectRemoved(want stack.AddressRemovalReason) error {
select {
case got := <-ad.removedCh:
if want != got {
return fmt.Errorf("dispatcher for nic=%d addr=%s got removal reason = %s, want = %s", ad.nicid, ad.addr, got, want)
}
default:
return fmt.Errorf("dispatcher for nic=%d addr=%s address removed event not immediately ready", ad.nicid, ad.addr)
}
return nil
}
func infiniteLifetimes() stack.AddressLifetimes {
return stack.AddressLifetimes{
Deprecated: false,
ValidUntil: tcpip.MonotonicTimeInfinite(),
PreferredUntil: tcpip.MonotonicTimeInfinite(),
}
}
func TestNetworkReceive(t *testing.T) {
// Create a stack with the fake network protocol, one nic, and two
// addresses attached to it: 1 & 2.
@@ -2297,7 +2408,7 @@ func TestAddProtocolAddress(t *testing.T) {
properties := stack.AddressProperties{
PEB: behavior,
ConfigType: configType,
Deprecated: deprecated,
Lifetimes: stack.AddressLifetimes{Deprecated: deprecated},
Temporary: temporary,
}
protocolAddr := tcpip.ProtocolAddress{
@@ -2687,8 +2798,10 @@ func TestNICAutoGenLinkLocalAddr(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
const autoGenAddrCount = 1
ndpDisp := ndpDispatcher{
autoGenAddrC: make(chan ndpAutoGenAddrEvent, 1),
autoGenAddrC: make(chan ndpAutoGenAddrEvent, autoGenAddrCount),
autoGenAddrNewC: make(chan ndpAutoGenAddrNewEvent, autoGenAddrCount),
}
opts := stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocolWithOptions(ipv6.Options{
@@ -2731,13 +2844,8 @@ func TestNICAutoGenLinkLocalAddr(t *testing.T) {
// Should have auto-generated an address and resolved immediately (DAD
// is disabled).
select {
case e := <-ndpDisp.autoGenAddrC:
if diff := checkAutoGenAddrEvent(e, expectedMainAddr, newAddr); diff != "" {
t.Errorf("auto-gen addr event mismatch (-want +got):\n%s", diff)
}
default:
t.Fatal("expected addr auto gen event")
if _, err := expectAutoGenAddrNewEvent(&ndpDisp, expectedMainAddr); err != nil {
t.Fatalf("error expecting link-local auto-gen address generated event: %s", err)
}
} else {
// Should not have auto-generated an address.
@@ -3190,7 +3298,7 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
{
addr: globalAddr2,
properties: stack.AddressProperties{
Deprecated: true,
Lifetimes: stack.AddressLifetimes{Deprecated: true},
},
},
},
@@ -3203,7 +3311,7 @@ func TestIPv6SourceAddressSelectionScopeAndSameAddress(t *testing.T) {
{
addr: globalAddr2,
properties: stack.AddressProperties{
Deprecated: true,
Lifetimes: stack.AddressLifetimes{Deprecated: true},
},
},
{addr: globalAddr1},
+7
View File
@@ -33,6 +33,7 @@ import (
"errors"
"fmt"
"io"
"math"
"math/bits"
"reflect"
"strconv"
@@ -76,6 +77,12 @@ func (mt MonotonicTime) String() string {
return strconv.FormatInt(mt.nanoseconds, 10)
}
// MonotonicTimeInfinite returns the monotonic timestamp as far away in the
// future as possible.
func MonotonicTimeInfinite() MonotonicTime {
return MonotonicTime{nanoseconds: math.MaxInt64}
}
// Before reports whether the monotonic clock reading mt is before u.
func (mt MonotonicTime) Before(u MonotonicTime) bool {
return mt.nanoseconds < u.nanoseconds
+2 -1
View File
@@ -54,7 +54,8 @@ func (*ndpDispatcher) OnOnLinkPrefixDiscovered(tcpip.NICID, tcpip.Subnet) {
func (*ndpDispatcher) OnOnLinkPrefixInvalidated(tcpip.NICID, tcpip.Subnet) {}
func (*ndpDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) {
func (*ndpDispatcher) OnAutoGenAddress(tcpip.NICID, tcpip.AddressWithPrefix) stack.AddressDispatcher {
return nil
}
func (*ndpDispatcher) OnAutoGenAddressDeprecated(tcpip.NICID, tcpip.AddressWithPrefix) {}