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
This commit is contained in:
commit
2c4ae3b0a4
712 changed files with 185689 additions and 0 deletions
950
pkg/tcpip/stack/addressable_endpoint_state.go
Normal file
950
pkg/tcpip/stack/addressable_endpoint_state.go
Normal file
|
|
@ -0,0 +1,950 @@
|
|||
// 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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue