mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+897
-519
File diff suppressed because it is too large
Load Diff
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
Reference in New Issue
Block a user