github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/distillery.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package policy
    16  
    17  import (
    18  	"fmt"
    19  	"sync/atomic"
    20  	"unsafe"
    21  
    22  	identityPkg "github.com/cilium/cilium/pkg/identity"
    23  	"github.com/cilium/cilium/pkg/identity/identitymanager"
    24  	"github.com/cilium/cilium/pkg/lock"
    25  )
    26  
    27  // SelectorPolicy represents a cached selectorPolicy, previously resolved from
    28  // the policy repository and ready to be distilled against a set of identities
    29  // to compute datapath-level policy configuration.
    30  type SelectorPolicy interface {
    31  	// Consume returns the policy in terms of connectivity to peer
    32  	// Identities. The callee MUST NOT modify the returned pointer.
    33  	Consume(owner PolicyOwner) *EndpointPolicy
    34  }
    35  
    36  // PolicyCache represents a cache of resolved policies for identities.
    37  type PolicyCache struct {
    38  	lock.Mutex
    39  
    40  	// repo is a circular reference back to the Repository, but as
    41  	// we create only one Repository and one PolicyCache for each
    42  	// Cilium Agent process, these will never need to be garbage
    43  	// collected.
    44  	repo     *Repository
    45  	policies map[identityPkg.NumericIdentity]*cachedSelectorPolicy
    46  }
    47  
    48  // NewPolicyCache creates a new cache of SelectorPolicy.
    49  func NewPolicyCache(repo *Repository, subscribe bool) *PolicyCache {
    50  	cache := &PolicyCache{
    51  		repo:     repo,
    52  		policies: make(map[identityPkg.NumericIdentity]*cachedSelectorPolicy),
    53  	}
    54  	if subscribe {
    55  		identitymanager.Subscribe(cache)
    56  	}
    57  	return cache
    58  }
    59  
    60  func (cache *PolicyCache) GetSelectorCache() *SelectorCache {
    61  	return cache.repo.GetSelectorCache()
    62  }
    63  
    64  // lookupOrCreate adds the specified Identity to the policy cache, with a reference
    65  // from the specified Endpoint, then returns the threadsafe copy of the policy
    66  // and whether policy has been computed for this identity.
    67  func (cache *PolicyCache) lookupOrCreate(identity *identityPkg.Identity, create bool) (SelectorPolicy, bool) {
    68  	cache.Lock()
    69  	defer cache.Unlock()
    70  	cip, ok := cache.policies[identity.ID]
    71  	if create && !ok {
    72  		cip = newCachedSelectorPolicy(identity, cache.repo.GetSelectorCache())
    73  		cache.policies[identity.ID] = cip
    74  	}
    75  	if cip != nil {
    76  		return cip, cip.getPolicy().Revision > 0
    77  	}
    78  	return nil, false
    79  }
    80  
    81  // insert adds the specified Identity to the policy cache, with a reference
    82  // from the specified Endpoint, then returns the threadsafe copy of the policy
    83  // and whether policy has been computed for this identity.
    84  func (cache *PolicyCache) insert(identity *identityPkg.Identity) (SelectorPolicy, bool) {
    85  	return cache.lookupOrCreate(identity, true)
    86  }
    87  
    88  // delete forgets about any cached SelectorPolicy that this endpoint uses.
    89  //
    90  // Returns true if the SelectorPolicy was removed from the cache.
    91  func (cache *PolicyCache) delete(identity *identityPkg.Identity) bool {
    92  	cache.Lock()
    93  	defer cache.Unlock()
    94  	cip, ok := cache.policies[identity.ID]
    95  	if ok {
    96  		delete(cache.policies, identity.ID)
    97  		cip.getPolicy().Detach()
    98  	}
    99  	return ok
   100  }
   101  
   102  // updateSelectorPolicy resolves the policy for the security identity of the
   103  // specified endpoint and stores it internally. It will skip policy resolution
   104  // if the cached policy is already at the revision specified in the repo.
   105  //
   106  // Returns whether the cache was updated, or an error.
   107  //
   108  // Must be called with repo.Mutex held for reading.
   109  func (cache *PolicyCache) updateSelectorPolicy(identity *identityPkg.Identity) (bool, error) {
   110  	cache.Lock()
   111  	cip, ok := cache.policies[identity.ID]
   112  	cache.Unlock()
   113  	if !ok {
   114  		return false, fmt.Errorf("SelectorPolicy not found in cache for ID %d", identity.ID)
   115  	}
   116  
   117  	// As long as UpdatePolicy() is triggered from endpoint
   118  	// regeneration, it's possible for two endpoints with the
   119  	// *same* identity to race to update the policy here. Such
   120  	// racing would lead to first of the endpoints using a
   121  	// selectorPolicy that is already detached from the selector
   122  	// cache, and thus not getting any incremental updates.
   123  	//
   124  	// Lock the 'cip' for the duration of the revision check and
   125  	// the possible policy update.
   126  	cip.Lock()
   127  	defer cip.Unlock()
   128  
   129  	// Don't resolve policy if it was already done for this or later revision.
   130  	if cip.getPolicy().Revision >= cache.repo.GetRevision() {
   131  		return false, nil
   132  	}
   133  
   134  	// Resolve the policies, which could fail
   135  	selPolicy, err := cache.repo.resolvePolicyLocked(identity)
   136  	if err != nil {
   137  		return false, err
   138  	}
   139  
   140  	cip.setPolicy(selPolicy)
   141  
   142  	return true, nil
   143  }
   144  
   145  // LocalEndpointIdentityAdded creates a SelectorPolicy cache entry for the
   146  // specified Identity, without calculating any policy for it.
   147  func (cache *PolicyCache) LocalEndpointIdentityAdded(identity *identityPkg.Identity) {
   148  	cache.insert(identity)
   149  }
   150  
   151  // LocalEndpointIdentityRemoved deletes the cached SelectorPolicy for the
   152  // specified Identity.
   153  func (cache *PolicyCache) LocalEndpointIdentityRemoved(identity *identityPkg.Identity) {
   154  	cache.delete(identity)
   155  }
   156  
   157  // Lookup attempts to locate the SelectorPolicy corresponding to the specified
   158  // identity. If policy is not cached for the identity, it returns nil.
   159  func (cache *PolicyCache) Lookup(identity *identityPkg.Identity) SelectorPolicy {
   160  	cip, _ := cache.lookupOrCreate(identity, false)
   161  	return cip
   162  }
   163  
   164  // UpdatePolicy resolves the policy for the security identity of the specified
   165  // endpoint and caches it for future use.
   166  //
   167  // The caller must provide threadsafety for iteration over the policy
   168  // repository.
   169  func (cache *PolicyCache) UpdatePolicy(identity *identityPkg.Identity) error {
   170  	_, err := cache.updateSelectorPolicy(identity)
   171  	return err
   172  }
   173  
   174  // cachedSelectorPolicy is a wrapper around a selectorPolicy (stored in the
   175  // 'policy' field). It is always nested directly in the owning policyCache,
   176  // and is protected against concurrent writes via the policyCache mutex.
   177  type cachedSelectorPolicy struct {
   178  	lock.Mutex // lock is needed to synchronize parallel policy updates
   179  
   180  	identity *identityPkg.Identity
   181  	policy   unsafe.Pointer
   182  }
   183  
   184  func newCachedSelectorPolicy(identity *identityPkg.Identity, selectorCache *SelectorCache) *cachedSelectorPolicy {
   185  	cip := &cachedSelectorPolicy{
   186  		identity: identity,
   187  	}
   188  	cip.setPolicy(newSelectorPolicy(0, selectorCache))
   189  	return cip
   190  }
   191  
   192  // getPolicy returns a reference to the selectorPolicy that is cached.
   193  //
   194  // Users should treat the result as immutable state that MUST NOT be modified.
   195  func (cip *cachedSelectorPolicy) getPolicy() *selectorPolicy {
   196  	return (*selectorPolicy)(atomic.LoadPointer(&cip.policy))
   197  }
   198  
   199  // setPolicy updates the reference to the SelectorPolicy that is cached.
   200  // Calls Detach() on the old policy, if any.
   201  func (cip *cachedSelectorPolicy) setPolicy(policy *selectorPolicy) {
   202  	oldPolicy := (*selectorPolicy)(atomic.SwapPointer(&cip.policy, unsafe.Pointer(policy)))
   203  	if oldPolicy != nil {
   204  		// Release the references the previous policy holds on the selector cache.
   205  		oldPolicy.Detach()
   206  	}
   207  }
   208  
   209  // Consume returns the EndpointPolicy that defines connectivity policy to
   210  // Identities in the specified cache.
   211  //
   212  // This denotes that a particular endpoint is 'consuming' the policy from the
   213  // selector policy cache.
   214  func (cip *cachedSelectorPolicy) Consume(owner PolicyOwner) *EndpointPolicy {
   215  	// TODO: This currently computes the EndpointPolicy from SelectorPolicy
   216  	// on-demand, however in future the cip is intended to cache the
   217  	// EndpointPolicy for this Identity and emit datapath deltas instead.
   218  	return cip.getPolicy().DistillPolicy(owner)
   219  }