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 }