github.com/imran-kn/cilium-fork@v1.6.9/pkg/envoy/resources.go (about) 1 // Copyright 2018 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 envoy 16 17 import ( 18 "net" 19 "sort" 20 "sync" 21 22 "github.com/cilium/cilium/pkg/envoy/xds" 23 "github.com/cilium/cilium/pkg/identity" 24 "github.com/cilium/cilium/pkg/ipcache" 25 "github.com/cilium/cilium/pkg/logging/logfields" 26 27 envoyAPI "github.com/cilium/proxy/go/cilium/api" 28 "github.com/sirupsen/logrus" 29 ) 30 31 const ( 32 // ListenerTypeURL is the type URL of Listener resources. 33 ListenerTypeURL = "type.googleapis.com/envoy.api.v2.Listener" 34 35 // NetworkPolicyTypeURL is the type URL of NetworkPolicy resources. 36 NetworkPolicyTypeURL = "type.googleapis.com/cilium.NetworkPolicy" 37 38 // NetworkPolicyHostsTypeURL is the type URL of NetworkPolicyHosts resources. 39 NetworkPolicyHostsTypeURL = "type.googleapis.com/cilium.NetworkPolicyHosts" 40 ) 41 42 // NPHDSCache is a cache of resources in the Network Policy Hosts Discovery 43 // Service. 44 type NPHDSCache struct { 45 *xds.Cache 46 } 47 48 func newNPHDSCache() NPHDSCache { 49 return NPHDSCache{Cache: xds.NewCache()} 50 } 51 52 var ( 53 // NetworkPolicyHostsCache is the global cache of resources of type 54 // NetworkPolicyHosts. Resources in this cache must have the 55 // NetworkPolicyHostsTypeURL type URL. 56 NetworkPolicyHostsCache = newNPHDSCache() 57 58 observerOnce = sync.Once{} 59 ) 60 61 // HandleResourceVersionAck is required to implement ResourceVersionAckObserver. 62 // We use this to start the IP Cache listener on the first ACK so that we only 63 // start the IP Cache listener if there is an Envoy node that uses NPHDS (e.g., 64 // Istio node, or host proxy running on kernel w/o LPM bpf map support). 65 func (cache *NPHDSCache) HandleResourceVersionAck(ackVersion uint64, nackVersion uint64, nodeIP string, resourceNames []string, typeURL string, detail string) { 66 // Start caching for IP/ID mappings on the first indication someone wants them 67 observerOnce.Do(func() { 68 ipcache.IPIdentityCache.AddListener(cache) 69 }) 70 } 71 72 // OnIPIdentityCacheGC is required to implement IPIdentityMappingListener. 73 func (cache *NPHDSCache) OnIPIdentityCacheGC() { 74 // We don't have anything to synchronize in this case. 75 } 76 77 // OnIPIdentityCacheChange pushes modifications to the IP<->Identity mapping 78 // into the Network Policy Host Discovery Service (NPHDS). 79 func (cache *NPHDSCache) OnIPIdentityCacheChange(modType ipcache.CacheModification, cidr net.IPNet, 80 oldHostIP, newHostIP net.IP, oldID *identity.NumericIdentity, newID identity.NumericIdentity, encryptKey uint8) { 81 // An upsert where an existing pair exists should translate into a 82 // delete (for the old Identity) followed by an upsert (for the new). 83 if oldID != nil && modType == ipcache.Upsert { 84 // Skip update if identity is identical 85 if *oldID == newID { 86 return 87 } 88 89 cache.OnIPIdentityCacheChange(ipcache.Delete, cidr, nil, nil, nil, *oldID, encryptKey) 90 } 91 92 cidrStr := cidr.String() 93 94 scopedLog := log.WithFields(logrus.Fields{ 95 logfields.IPAddr: cidrStr, 96 logfields.Identity: newID, 97 logfields.Modification: modType, 98 }) 99 100 // Look up the current resources for the specified Identity. 101 resourceName := newID.StringID() 102 msg, err := cache.Lookup(NetworkPolicyHostsTypeURL, resourceName) 103 if err != nil { 104 scopedLog.WithError(err).Warning("Can't lookup NPHDS cache") 105 return 106 } 107 108 switch modType { 109 case ipcache.Upsert: 110 var hostAddresses []string 111 if msg == nil { 112 hostAddresses = make([]string, 0, 1) 113 } else { 114 // If the resource already exists, create a copy of it and insert 115 // the new IP address into its HostAddresses list. 116 npHost := msg.(*envoyAPI.NetworkPolicyHosts) 117 hostAddresses = make([]string, 0, len(npHost.HostAddresses)+1) 118 hostAddresses = append(hostAddresses, npHost.HostAddresses...) 119 } 120 hostAddresses = append(hostAddresses, cidrStr) 121 sort.Strings(hostAddresses) 122 123 newNpHost := envoyAPI.NetworkPolicyHosts{ 124 Policy: uint64(newID), 125 HostAddresses: hostAddresses, 126 } 127 if err := newNpHost.Validate(); err != nil { 128 scopedLog.WithError(err).WithFields(logrus.Fields{ 129 logfields.XDSResource: newNpHost, 130 }).Warning("Could not validate NPHDS resource update on upsert") 131 return 132 } 133 cache.Upsert(NetworkPolicyHostsTypeURL, resourceName, &newNpHost) 134 case ipcache.Delete: 135 if msg == nil { 136 // Doesn't exist; already deleted. 137 return 138 } 139 cache.handleIPDelete(msg.(*envoyAPI.NetworkPolicyHosts), resourceName, cidrStr) 140 } 141 } 142 143 // handleIPUpsert deletes elements from the NPHDS cache with the specified peer IP->ID mapping. 144 func (cache *NPHDSCache) handleIPDelete(npHost *envoyAPI.NetworkPolicyHosts, peerIdentity, peerIP string) { 145 targetIndex := -1 146 147 scopedLog := log.WithFields(logrus.Fields{ 148 logfields.IPAddr: peerIP, 149 logfields.Identity: peerIdentity, 150 logfields.Modification: ipcache.Delete, 151 }) 152 for i, endpointIP := range npHost.HostAddresses { 153 if endpointIP == peerIP { 154 targetIndex = i 155 break 156 } 157 } 158 if targetIndex < 0 { 159 scopedLog.Warning("Can't find IP in NPHDS cache") 160 return 161 } 162 163 // If removing this host would result in empty list, delete it. 164 // Otherwise, update to a list that doesn't contain the target IP 165 if len(npHost.HostAddresses) <= 1 { 166 cache.Delete(NetworkPolicyHostsTypeURL, peerIdentity) 167 } else { 168 // If the resource is to be updated, create a copy of it before 169 // removing the IP address from its HostAddresses list. 170 hostAddresses := make([]string, 0, len(npHost.HostAddresses)-1) 171 if len(npHost.HostAddresses) == targetIndex { 172 hostAddresses = append(hostAddresses, npHost.HostAddresses[0:targetIndex]...) 173 } else { 174 hostAddresses = append(hostAddresses, npHost.HostAddresses[0:targetIndex]...) 175 hostAddresses = append(hostAddresses, npHost.HostAddresses[targetIndex+1:]...) 176 } 177 178 newNpHost := envoyAPI.NetworkPolicyHosts{ 179 Policy: uint64(npHost.Policy), 180 HostAddresses: hostAddresses, 181 } 182 if err := newNpHost.Validate(); err != nil { 183 scopedLog.WithError(err).Warning("Could not validate NPHDS resource update on delete") 184 return 185 } 186 cache.Upsert(NetworkPolicyHostsTypeURL, peerIdentity, &newNpHost) 187 } 188 }