gvisor-lx/pkg/tcpip/stack/addressable_endpoint_state.go
Leadaxe 2c4ae3b0a4 snapshot: sagernet/gvisor v0.0.0-20250811.0-sing-box-mod.1
Содержимое пина, зафиксированного в go.mod sing-box-lx, одним коммитом
без истории. Полная история SagerNet/gvisor — 1.45 ГБ и клонируется в
каждой CI-джобе; наша дельта — одна вставка в одну функцию, история для
неё не нужна.

Module path github.com/sagernet/gvisor сохранён намеренно: на него
опирается replace-директива суперпроекта.

Патч поверх — отдельным коммитом, чтобы дельта читалась одним git show
и переносилась на новый пин копированием.

SPECS/TASKS/048-GVISOR_HANDSHAKE_NIL_CRASH
2026-08-04 15:50:08 +03:00

950 lines
28 KiB
Go

// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package stack
import (
"fmt"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/header"
)
func (lifetimes *AddressLifetimes) sanitize() {
if lifetimes.Deprecated {
lifetimes.PreferredUntil = tcpip.MonotonicTime{}
}
}
var _ AddressableEndpoint = (*AddressableEndpointState)(nil)
// AddressableEndpointState is an implementation of an AddressableEndpoint.
//
// +stateify savable
type AddressableEndpointState struct {
networkEndpoint NetworkEndpoint
options AddressableEndpointStateOptions
// Lock ordering (from outer to inner lock ordering):
//
// AddressableEndpointState.mu
// addressState.mu
mu addressableEndpointStateRWMutex `state:"nosave"`
// TODO(b/361075310): Enable s/r for the below fields.
//
// +checklocks:mu
endpoints map[tcpip.Address]*addressState `state:"nosave"`
// +checklocks:mu
primary []*addressState `state:"nosave"`
}
// AddressableEndpointStateOptions contains options used to configure an
// AddressableEndpointState.
//
// +stateify savable
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, 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
// address is considered bound to the endpoint.
//
// Returns nil if the passed address is not associated with the endpoint.
func (a *AddressableEndpointState) GetAddress(addr tcpip.Address) AddressEndpoint {
a.mu.RLock()
defer a.mu.RUnlock()
ep, ok := a.endpoints[addr]
if !ok {
return nil
}
return ep
}
// ForEachEndpoint calls f for each address.
//
// Once f returns false, f will no longer be called.
func (a *AddressableEndpointState) ForEachEndpoint(f func(AddressEndpoint) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, ep := range a.endpoints {
if !f(ep) {
return
}
}
}
// ForEachPrimaryEndpoint calls f for each primary address.
//
// Once f returns false, f will no longer be called.
func (a *AddressableEndpointState) ForEachPrimaryEndpoint(f func(AddressEndpoint) bool) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, ep := range a.primary {
if !f(ep) {
return
}
}
}
func (a *AddressableEndpointState) releaseAddressState(addrState *addressState) {
a.mu.Lock()
defer a.mu.Unlock()
a.releaseAddressStateLocked(addrState)
}
// releaseAddressStateLocked removes addrState from a's address state
// (primary and endpoints list).
//
// +checklocks:a.mu
func (a *AddressableEndpointState) releaseAddressStateLocked(addrState *addressState) {
oldPrimary := a.primary
for i, s := range a.primary {
if s == addrState {
a.primary = append(a.primary[:i], a.primary[i+1:]...)
oldPrimary[len(oldPrimary)-1] = nil
break
}
}
delete(a.endpoints, addrState.addr.Address)
}
// AddAndAcquirePermanentAddress implements AddressableEndpoint.
func (a *AddressableEndpointState) AddAndAcquirePermanentAddress(addr tcpip.AddressWithPrefix, properties AddressProperties) (AddressEndpoint, tcpip.Error) {
return a.AddAndAcquireAddress(addr, properties, Permanent)
}
// AddAndAcquireTemporaryAddress adds a temporary address.
//
// Returns *tcpip.ErrDuplicateAddress if the address exists.
//
// 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, properties, kind)
// 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
}
// addAndAcquireAddressLocked adds, acquires and returns a permanent or
// temporary address.
//
// If the addressable endpoint already has the address in a non-permanent state,
// and addAndAcquireAddressLocked is adding a permanent address, that address is
// promoted in place and its properties set to the properties provided. If the
// address already exists in any other state, then *tcpip.ErrDuplicateAddress is
// returned, regardless the kind of address that is being added.
//
// +checklocks:a.mu
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
addrState, ok := a.endpoints[addr.Address]
if ok {
if !permanent {
// We are adding a non-permanent address but the address exists. No need
// to go any further since we can only promote existing temporary/expired
// addresses to permanent.
return nil, &tcpip.ErrDuplicateAddress{}
}
addrState.mu.RLock()
if addrState.refs.ReadRefs() == 0 {
panic(fmt.Sprintf("found an address that should have been released (ref count == 0); address = %s", addrState.addr))
}
isPermanent := addrState.kind.IsPermanent()
addrState.mu.RUnlock()
if isPermanent {
// We are adding a permanent address but a permanent address already
// exists.
return nil, &tcpip.ErrDuplicateAddress{}
}
// We now promote the address.
for i, s := range a.primary {
if s == addrState {
switch properties.PEB {
case CanBePrimaryEndpoint:
// The address is already in the primary address list.
attemptAddToPrimary = false
case FirstPrimaryEndpoint:
if i == 0 {
// The address is already first in the primary address list.
attemptAddToPrimary = false
} else {
a.primary = append(a.primary[:i], a.primary[i+1:]...)
}
case NeverPrimaryEndpoint:
a.primary = append(a.primary[:i], a.primary[i+1:]...)
default:
panic(fmt.Sprintf("unrecognized primary endpoint behaviour = %d", properties.PEB))
}
break
}
}
addrState.refs.IncRef()
} else {
addrState = &addressState{
addressableEndpointState: a,
addr: addr,
temporary: properties.Temporary,
// Cache the subnet in addrState to avoid calls to addr.Subnet() as that
// results in allocations on every call.
subnet: addr.Subnet(),
}
addrState.refs.InitRefs()
a.endpoints[addr.Address] = addrState
// 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
}
// At this point we have an address we are either promoting from an expired or
// temporary address to permanent, promoting an expired address to temporary,
// or we are adding a new temporary or permanent address.
//
// The address MUST be write locked at this point.
addrState.mu.Lock()
defer addrState.mu.Unlock()
if permanent {
if addrState.kind.IsPermanent() {
panic(fmt.Sprintf("only non-permanent addresses should be promoted to permanent; address = %s", addrState.addr))
}
// Primary addresses are biased by 1.
addrState.refs.IncRef()
addrState.kind = kind
}
addrState.configType = properties.ConfigType
lifetimes := properties.Lifetimes
lifetimes.sanitize()
addrState.lifetimes = lifetimes
addrState.disp = properties.Disp
if attemptAddToPrimary {
switch properties.PEB {
case NeverPrimaryEndpoint:
case CanBePrimaryEndpoint:
a.primary = append(a.primary, addrState)
case FirstPrimaryEndpoint:
if cap(a.primary) == len(a.primary) {
a.primary = append([]*addressState{addrState}, a.primary...)
} else {
// Shift all the endpoints by 1 to make room for the new address at the
// front. We could have just created a new slice but this saves
// allocations when the slice has capacity for the new address.
primaryCount := len(a.primary)
a.primary = append(a.primary, nil)
if n := copy(a.primary[1:], a.primary); n != primaryCount {
panic(fmt.Sprintf("copied %d elements; expected = %d elements", n, primaryCount))
}
a.primary[0] = addrState
}
default:
panic(fmt.Sprintf("unrecognized primary endpoint behaviour = %d", properties.PEB))
}
}
addrState.notifyChangedLocked()
return addrState, nil
}
// RemovePermanentAddress implements AddressableEndpoint.
func (a *AddressableEndpointState) RemovePermanentAddress(addr tcpip.Address) tcpip.Error {
a.mu.Lock()
defer a.mu.Unlock()
return a.removePermanentAddressLocked(addr)
}
// removePermanentAddressLocked is like RemovePermanentAddress but with locking
// requirements.
//
// +checklocks:a.mu
func (a *AddressableEndpointState) removePermanentAddressLocked(addr tcpip.Address) tcpip.Error {
addrState, ok := a.endpoints[addr]
if !ok {
return &tcpip.ErrBadLocalAddress{}
}
return a.removePermanentEndpointLocked(addrState, AddressRemovalManualAction)
}
// RemovePermanentEndpoint removes the passed endpoint if it is associated with
// a and permanent.
func (a *AddressableEndpointState) RemovePermanentEndpoint(ep AddressEndpoint, reason AddressRemovalReason) tcpip.Error {
addrState, ok := ep.(*addressState)
if !ok || addrState.addressableEndpointState != a {
return &tcpip.ErrInvalidEndpointState{}
}
a.mu.Lock()
defer a.mu.Unlock()
return a.removePermanentEndpointLocked(addrState, reason)
}
// removePermanentAddressLocked is like RemovePermanentAddress but with locking
// requirements.
//
// +checklocks:a.mu
func (a *AddressableEndpointState) removePermanentEndpointLocked(addrState *addressState, reason AddressRemovalReason) tcpip.Error {
if !addrState.GetKind().IsPermanent() {
return &tcpip.ErrBadLocalAddress{}
}
addrState.remove(reason)
a.decAddressRefLocked(addrState)
return nil
}
// decAddressRef decrements the address's reference count and releases it once
// the reference count hits 0.
func (a *AddressableEndpointState) decAddressRef(addrState *addressState) {
a.mu.Lock()
defer a.mu.Unlock()
a.decAddressRefLocked(addrState)
}
// decAddressRefLocked is like decAddressRef but with locking requirements.
//
// +checklocks:a.mu
func (a *AddressableEndpointState) decAddressRefLocked(addrState *addressState) {
destroy := false
addrState.refs.DecRef(func() {
destroy = true
})
if !destroy {
return
}
addrState.mu.Lock()
defer addrState.mu.Unlock()
// A non-expired permanent address must not have its reference count dropped
// to 0.
if addrState.kind.IsPermanent() {
panic(fmt.Sprintf("permanent addresses should be removed through the AddressableEndpoint: addr = %s, kind = %d", addrState.addr, addrState.kind))
}
a.releaseAddressStateLocked(addrState)
}
// SetDeprecated implements stack.AddressableEndpoint.
func (a *AddressableEndpointState) SetDeprecated(addr tcpip.Address, deprecated bool) tcpip.Error {
a.mu.RLock()
defer a.mu.RUnlock()
addrState, ok := a.endpoints[addr]
if !ok {
return &tcpip.ErrBadLocalAddress{}
}
addrState.SetDeprecated(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()
defer a.mu.RUnlock()
ep := a.acquirePrimaryAddressRLocked(tcpip.Address{}, tcpip.Address{} /* srcHint */, func(ep *addressState) bool {
switch kind := ep.GetKind(); kind {
case Permanent:
return a.networkEndpoint.Enabled() || !a.options.HiddenWhileDisabled
case PermanentTentative, PermanentExpired, Temporary:
return false
default:
panic(fmt.Sprintf("unknown address kind: %d", kind))
}
})
if ep == nil {
return tcpip.AddressWithPrefix{}
}
addr := ep.AddressWithPrefix()
// Note that when ep must have a ref count >=2, because its ref count
// must be >=1 in order to be found and the ref count was incremented
// when a reference was acquired. The only way for the ref count to
// drop below 2 is for the endpoint to be removed, which requires a
// write lock; so we're guaranteed to be able to decrement the ref
// count and not need to remove the endpoint from a.primary.
ep.decRefMustNotFree()
return addr
}
// acquirePrimaryAddressRLocked returns an acquired primary address that is
// valid according to isValid.
//
// +checklocksread:a.mu
func (a *AddressableEndpointState) acquirePrimaryAddressRLocked(remoteAddr, srcHint tcpip.Address, isValid func(*addressState) bool) *addressState {
// TODO: Move this out into IPv4-specific code.
// IPv6 handles source IP selection elsewhere. We have to do source
// selection only for IPv4, in which case ep is never deprecated. Thus
// we don't have to worry about refcounts.
if remoteAddr.Len() == header.IPv4AddressSize && remoteAddr != (tcpip.Address{}) {
var best *addressState
var bestLen uint8
for _, state := range a.primary {
if !isValid(state) {
continue
}
// Source hint takes precedent over prefix matching.
if state.addr.Address == srcHint && srcHint != (tcpip.Address{}) {
best = state
break
}
stateLen := state.addr.Address.MatchingPrefix(remoteAddr)
if best == nil || bestLen < stateLen {
best = state
bestLen = stateLen
}
}
if best != nil && best.TryIncRef() {
return best
}
}
var deprecatedEndpoint *addressState
for _, ep := range a.primary {
if !isValid(ep) {
continue
}
if !ep.Deprecated() {
if ep.TryIncRef() {
// ep is not deprecated, so return it immediately.
//
// If we kept track of a deprecated endpoint, decrement its reference
// count since it was incremented when we decided to keep track of it.
if deprecatedEndpoint != nil {
// Note that when deprecatedEndpoint was found, its ref count
// must have necessarily been >=1, and after incrementing it
// must be >=2. The only way for the ref count to drop below 2 is
// for the endpoint to be removed, which requires a write lock;
// so we're guaranteed to be able to decrement the ref count
// and not need to remove the endpoint from a.primary.
deprecatedEndpoint.decRefMustNotFree()
}
return ep
}
} else if deprecatedEndpoint == nil && ep.TryIncRef() {
// We prefer an endpoint that is not deprecated, but we keep track of
// ep in case a doesn't have any non-deprecated endpoints.
//
// If we end up finding a more preferred endpoint, ep's reference count
// will be decremented.
deprecatedEndpoint = ep
}
}
return deprecatedEndpoint
}
// AcquireAssignedAddressOrMatching returns an address endpoint that is
// considered assigned to the addressable endpoint.
//
// If the address is an exact match with an existing address, that address is
// returned. Otherwise, if f is provided, f is called with each address and
// the address that f returns true for is returned.
//
// If there is no matching address, a temporary address will be returned if
// allowTemp is true.
//
// If readOnly is true, the address will be returned without an extra reference.
// In this case it is not safe to modify the endpoint, only read attributes like
// subnet.
//
// Regardless how the address was obtained, it will be acquired before it is
// returned.
func (a *AddressableEndpointState) AcquireAssignedAddressOrMatching(localAddr tcpip.Address, f func(AddressEndpoint) bool, allowTemp bool, tempPEB PrimaryEndpointBehavior, readOnly bool) AddressEndpoint {
lookup := func() *addressState {
if addrState, ok := a.endpoints[localAddr]; ok {
if !addrState.IsAssigned(allowTemp) {
return nil
}
if !readOnly && !addrState.TryIncRef() {
panic(fmt.Sprintf("failed to increase the reference count for address = %s", addrState.addr))
}
return addrState
}
if f != nil {
for _, addrState := range a.endpoints {
if addrState.IsAssigned(allowTemp) && f(addrState) {
if !readOnly && !addrState.TryIncRef() {
continue
}
return addrState
}
}
}
return nil
}
// Avoid exclusive lock on mu unless we need to add a new address.
a.mu.RLock()
ep := lookup()
a.mu.RUnlock()
if ep != nil {
return ep
}
if !allowTemp {
return nil
}
// Acquire state lock in exclusive mode as we need to add a new temporary
// endpoint.
a.mu.Lock()
defer a.mu.Unlock()
// Do the lookup again in case another goroutine added the address in the time
// we released and acquired the lock.
ep = lookup()
if ep != nil {
return ep
}
// Proceed to add a new temporary endpoint.
addr := localAddr.WithPrefix()
ep, err := a.addAndAcquireAddressLocked(addr, AddressProperties{PEB: tempPEB, Temporary: true}, 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
// expect no error.
panic(fmt.Sprintf("a.addAndAcquireAddressLocked(%s, AddressProperties{PEB: %s}, false): %s", addr, tempPEB, err))
}
// 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
}
if readOnly {
if ep.addressableEndpointState == a {
// Checklocks doesn't understand that we are logically guaranteed to have
// ep.mu locked already. We need to use checklocksignore to appease the
// analyzer.
ep.addressableEndpointState.decAddressRefLocked(ep) // +checklocksignore
} else {
ep.DecRef()
}
}
return ep
}
// AcquireAssignedAddress implements AddressableEndpoint.
func (a *AddressableEndpointState) AcquireAssignedAddress(localAddr tcpip.Address, allowTemp bool, tempPEB PrimaryEndpointBehavior, readOnly bool) AddressEndpoint {
return a.AcquireAssignedAddressOrMatching(localAddr, nil, allowTemp, tempPEB, readOnly)
}
// AcquireOutgoingPrimaryAddress implements AddressableEndpoint.
func (a *AddressableEndpointState) AcquireOutgoingPrimaryAddress(remoteAddr tcpip.Address, srcHint tcpip.Address, allowExpired bool) AddressEndpoint {
a.mu.Lock()
defer a.mu.Unlock()
ep := a.acquirePrimaryAddressRLocked(remoteAddr, srcHint, func(ep *addressState) bool {
return ep.IsAssigned(allowExpired)
})
// 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 acquirePrimaryAddressLocked 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
}
return ep
}
// PrimaryAddresses implements AddressableEndpoint.
func (a *AddressableEndpointState) PrimaryAddresses() []tcpip.AddressWithPrefix {
a.mu.RLock()
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
// to avoid confusion and prevent the caller from using
// those.
case PermanentTentative, PermanentExpired, Temporary:
continue
case Permanent:
default:
panic(fmt.Sprintf("address %s has unknown kind %d", ep.AddressWithPrefix(), kind))
}
addrs = append(addrs, ep.AddressWithPrefix())
}
return addrs
}
// PermanentAddresses implements AddressableEndpoint.
func (a *AddressableEndpointState) PermanentAddresses() []tcpip.AddressWithPrefix {
a.mu.RLock()
defer a.mu.RUnlock()
var addrs []tcpip.AddressWithPrefix
for _, ep := range a.endpoints {
if !ep.GetKind().IsPermanent() {
continue
}
addrs = append(addrs, ep.AddressWithPrefix())
}
return addrs
}
// Cleanup forcefully leaves all groups and removes all permanent addresses.
func (a *AddressableEndpointState) Cleanup() {
a.mu.Lock()
defer a.mu.Unlock()
for _, ep := range a.endpoints {
// removePermanentEndpointLocked returns *tcpip.ErrBadLocalAddress if ep is
// not a permanent address.
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))
}
}
}
var _ AddressEndpoint = (*addressState)(nil)
// addressState holds state for an address.
type addressState struct {
addressableEndpointState *AddressableEndpointState
addr tcpip.AddressWithPrefix
subnet tcpip.Subnet
temporary bool
// Lock ordering (from outer to inner lock ordering):
//
// AddressableEndpointState.mu
// addressState.mu
mu addressStateRWMutex
refs addressStateRefs
// checklocks:mu
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
lifetimes AddressLifetimes
// The enclosing mutex must be write-locked before calling methods on the
// dispatcher.
//
// checklocks:mu
disp AddressDispatcher
}
// AddressWithPrefix implements AddressEndpoint.
func (a *addressState) AddressWithPrefix() tcpip.AddressWithPrefix {
return a.addr
}
// Subnet implements AddressEndpoint.
func (a *addressState) Subnet() tcpip.Subnet {
return a.subnet
}
// GetKind implements AddressEndpoint.
func (a *addressState) GetKind() AddressKind {
a.mu.RLock()
defer a.mu.RUnlock()
return a.kind
}
// SetKind implements AddressEndpoint.
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 {
switch kind := a.GetKind(); kind {
case PermanentTentative:
return false
case PermanentExpired:
return allowExpired
case Permanent, Temporary:
return true
default:
panic(fmt.Sprintf("address %s has unknown kind %d", a.AddressWithPrefix(), kind))
}
}
// IncRef implements AddressEndpoint.
func (a *addressState) TryIncRef() bool {
return a.refs.TryIncRef()
}
// DecRef implements AddressEndpoint.
func (a *addressState) DecRef() {
a.addressableEndpointState.decAddressRef(a)
}
// decRefMustNotFree decreases the reference count with the guarantee that the
// reference count will be greater than 0 after the decrement.
//
// Panics if the ref count is less than 2 after acquiring the lock in this
// function.
func (a *addressState) decRefMustNotFree() {
a.refs.DecRef(func() {
panic(fmt.Sprintf("cannot decrease addressState %s without freeing the endpoint", a.addr))
})
}
// ConfigType implements AddressEndpoint.
func (a *addressState) ConfigType() AddressConfigType {
a.mu.RLock()
defer a.mu.RUnlock()
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()
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.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()
}
}