github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/ipcache/ipcache.go (about)

     1  // Copyright 2018-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 ipcache
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  
    21  	"github.com/cilium/cilium/pkg/identity"
    22  	"github.com/cilium/cilium/pkg/lock"
    23  	"github.com/cilium/cilium/pkg/logging/logfields"
    24  	"github.com/cilium/cilium/pkg/option"
    25  	"github.com/cilium/cilium/pkg/source"
    26  
    27  	"github.com/sirupsen/logrus"
    28  )
    29  
    30  var (
    31  	// IPIdentityCache caches the mapping of endpoint IPs to their corresponding
    32  	// security identities across the entire cluster in which this instance of
    33  	// Cilium is running.
    34  	IPIdentityCache = NewIPCache()
    35  )
    36  
    37  // Identity is the identity representation of an IP<->Identity cache.
    38  type Identity struct {
    39  	// ID is the numeric identity
    40  	ID identity.NumericIdentity
    41  
    42  	// Source is the source of the identity in the cache
    43  	Source source.Source
    44  
    45  	// shadowed determines if another entry overlaps with this one.
    46  	// Shadowed identities are not propagated to listeners by default.
    47  	// Most commonly set for Identity with Source = source.Generated when
    48  	// a pod IP (other source) has the same IP.
    49  	shadowed bool
    50  }
    51  
    52  // IPKeyPair is the (IP, key) pair used of the identity
    53  type IPKeyPair struct {
    54  	IP  net.IP
    55  	Key uint8
    56  }
    57  
    58  // IPCache is a collection of mappings:
    59  // - mapping of endpoint IP or CIDR to security identities of all endpoints
    60  //   which are part of the same cluster, and vice-versa
    61  // - mapping of endpoint IP or CIDR to host IP (maybe nil)
    62  type IPCache struct {
    63  	mutex             lock.SemaphoredMutex
    64  	ipToIdentityCache map[string]Identity
    65  	identityToIPCache map[identity.NumericIdentity]map[string]struct{}
    66  	ipToHostIPCache   map[string]IPKeyPair
    67  
    68  	// prefixLengths reference-count the number of CIDRs that use
    69  	// particular prefix lengths for the mask.
    70  	v4PrefixLengths map[int]int
    71  	v6PrefixLengths map[int]int
    72  
    73  	listeners []IPIdentityMappingListener
    74  }
    75  
    76  // Implementation represents a concrete datapath implementation of the IPCache
    77  // which may restrict the ability to apply IPCache mappings, depending on the
    78  // underlying details of that implementation.
    79  type Implementation interface {
    80  	GetMaxPrefixLengths(ipv6 bool) int
    81  }
    82  
    83  // NewIPCache returns a new IPCache with the mappings of endpoint IP to security
    84  // identity (and vice-versa) initialized.
    85  func NewIPCache() *IPCache {
    86  	return &IPCache{
    87  		mutex:             lock.NewSemaphoredMutex(),
    88  		ipToIdentityCache: map[string]Identity{},
    89  		identityToIPCache: map[identity.NumericIdentity]map[string]struct{}{},
    90  		ipToHostIPCache:   map[string]IPKeyPair{},
    91  		v4PrefixLengths:   map[int]int{},
    92  		v6PrefixLengths:   map[int]int{},
    93  	}
    94  }
    95  
    96  // Lock locks the IPCache's mutex.
    97  func (ipc *IPCache) Lock() {
    98  	ipc.mutex.Lock()
    99  }
   100  
   101  // Unlock unlocks the IPCache's mutex.
   102  func (ipc *IPCache) Unlock() {
   103  	ipc.mutex.Unlock()
   104  }
   105  
   106  // RLock RLocks the IPCache's mutex.
   107  func (ipc *IPCache) RLock() {
   108  	ipc.mutex.RLock()
   109  }
   110  
   111  // RUnlock RUnlocks the IPCache's mutex.
   112  func (ipc *IPCache) RUnlock() {
   113  	ipc.mutex.RUnlock()
   114  }
   115  
   116  // SetListeners sets the listeners for this IPCache.
   117  func (ipc *IPCache) SetListeners(listeners []IPIdentityMappingListener) {
   118  	ipc.mutex.Lock()
   119  	ipc.listeners = listeners
   120  	ipc.mutex.Unlock()
   121  }
   122  
   123  // AddListener adds a listener for this IPCache.
   124  func (ipc *IPCache) AddListener(listener IPIdentityMappingListener) {
   125  	// We need to acquire the semaphored mutex as we Write Lock as we are
   126  	// modifying the listeners slice.
   127  	ipc.mutex.Lock()
   128  	ipc.listeners = append(ipc.listeners, listener)
   129  	// We will release the semaphore mutex with UnlockToRLock, *and not Unlock*
   130  	// because want to prevent a race across an Upsert or Delete. By doing this
   131  	// we are sure no other writers are performing any operation while we are
   132  	// still reading.
   133  	ipc.mutex.UnlockToRLock()
   134  	defer ipc.mutex.RUnlock()
   135  	// Initialize new listener with the current mappings
   136  	ipc.DumpToListenerLocked(listener)
   137  }
   138  
   139  func checkPrefixLengthsAgainstMap(impl Implementation, prefixes []*net.IPNet, existingPrefixes map[int]int, isIPv6 bool) error {
   140  	prefixLengths := make(map[int]struct{})
   141  
   142  	for i := range existingPrefixes {
   143  		prefixLengths[i] = struct{}{}
   144  	}
   145  
   146  	for _, prefix := range prefixes {
   147  		ones, bits := prefix.Mask.Size()
   148  		if _, ok := prefixLengths[ones]; !ok {
   149  			if bits == net.IPv6len*8 && isIPv6 || bits == net.IPv4len*8 && !isIPv6 {
   150  				prefixLengths[ones] = struct{}{}
   151  			}
   152  		}
   153  	}
   154  
   155  	maxPrefixLengths := impl.GetMaxPrefixLengths(isIPv6)
   156  	if len(prefixLengths) > maxPrefixLengths {
   157  		existingPrefixLengths := len(existingPrefixes)
   158  		return fmt.Errorf("adding specified CIDR prefixes would result in too many prefix lengths (current: %d, result: %d, max: %d)",
   159  			existingPrefixLengths, len(prefixLengths), maxPrefixLengths)
   160  	}
   161  	return nil
   162  }
   163  
   164  // checkPrefixes ensures that we will reject rules if the import of those
   165  // rules would cause the underlying implementation of the ipcache to exceed
   166  // the maximum number of supported CIDR prefix lengths.
   167  func checkPrefixes(impl Implementation, prefixes []*net.IPNet) (err error) {
   168  	IPIdentityCache.RLock()
   169  	defer IPIdentityCache.RUnlock()
   170  
   171  	if err = checkPrefixLengthsAgainstMap(impl, prefixes, IPIdentityCache.v4PrefixLengths, false); err != nil {
   172  		return
   173  	}
   174  	return checkPrefixLengthsAgainstMap(impl, prefixes, IPIdentityCache.v6PrefixLengths, true)
   175  }
   176  
   177  // refPrefixLength adds one reference to the prefix length in the map.
   178  func refPrefixLength(prefixLengths map[int]int, length int) {
   179  	if _, ok := prefixLengths[length]; ok {
   180  		prefixLengths[length]++
   181  	} else {
   182  		prefixLengths[length] = 1
   183  	}
   184  }
   185  
   186  // refPrefixLength removes one reference from the prefix length in the map.
   187  func unrefPrefixLength(prefixLengths map[int]int, length int) {
   188  	value := prefixLengths[length]
   189  	if value <= 1 {
   190  		delete(prefixLengths, length)
   191  	} else {
   192  		prefixLengths[length]--
   193  	}
   194  }
   195  
   196  // endpointIPToCIDR converts the endpoint IP into an equivalent full CIDR.
   197  func endpointIPToCIDR(ip net.IP) *net.IPNet {
   198  	bits := net.IPv6len * 8
   199  	if ip.To4() != nil {
   200  		bits = net.IPv4len * 8
   201  	}
   202  	return &net.IPNet{
   203  		IP:   ip,
   204  		Mask: net.CIDRMask(bits, bits),
   205  	}
   206  }
   207  
   208  func (ipc *IPCache) getHostIPCache(ip string) (net.IP, uint8) {
   209  	ipKeyPair := ipc.ipToHostIPCache[ip]
   210  	return ipKeyPair.IP, ipKeyPair.Key
   211  }
   212  
   213  // Upsert adds / updates the provided IP (endpoint or CIDR prefix) and identity
   214  // into the IPCache.
   215  //
   216  // Returns false if the entry is not owned by the self declared source, i.e.
   217  // returns false if the kubernetes layer is trying to upsert an entry now
   218  // managed by the kvstore layer. See source.AllowOverwrite() for rules on
   219  // ownership. hostIP is the location of the given IP. It is optional (may be
   220  // nil) and is propagated to the listeners.
   221  func (ipc *IPCache) Upsert(ip string, hostIP net.IP, hostKey uint8, newIdentity Identity) bool {
   222  	scopedLog := log
   223  	if option.Config.Debug {
   224  		scopedLog = log.WithFields(logrus.Fields{
   225  			logfields.IPAddr:   ip,
   226  			logfields.Identity: newIdentity,
   227  			logfields.Key:      hostKey,
   228  		})
   229  	}
   230  
   231  	ipc.mutex.Lock()
   232  	defer ipc.mutex.Unlock()
   233  
   234  	var cidr *net.IPNet
   235  	var oldIdentity *identity.NumericIdentity
   236  	callbackListeners := true
   237  
   238  	oldHostIP, oldHostKey := ipc.getHostIPCache(ip)
   239  
   240  	cachedIdentity, found := ipc.ipToIdentityCache[ip]
   241  	if found {
   242  		if !source.AllowOverwrite(cachedIdentity.Source, newIdentity.Source) {
   243  			return false
   244  		}
   245  
   246  		// Skip update if IP is already mapped to the given identity
   247  		// and the host IP hasn't changed.
   248  		if cachedIdentity == newIdentity && oldHostIP.Equal(hostIP) && hostKey == oldHostKey {
   249  			return true
   250  		}
   251  
   252  		oldIdentity = &cachedIdentity.ID
   253  	}
   254  
   255  	// Endpoint IP identities take precedence over CIDR identities, so if the
   256  	// IP is a full CIDR prefix and there's an existing equivalent endpoint IP,
   257  	// don't notify the listeners.
   258  	var err error
   259  	if _, cidr, err = net.ParseCIDR(ip); err == nil {
   260  		// Add a reference for the prefix length if this is a CIDR.
   261  		pl, bits := cidr.Mask.Size()
   262  		switch bits {
   263  		case net.IPv6len * 8:
   264  			refPrefixLength(ipc.v6PrefixLengths, pl)
   265  		case net.IPv4len * 8:
   266  			refPrefixLength(ipc.v4PrefixLengths, pl)
   267  		}
   268  
   269  		ones, bits := cidr.Mask.Size()
   270  		if ones == bits {
   271  			if _, endpointIPFound := ipc.ipToIdentityCache[cidr.IP.String()]; endpointIPFound {
   272  				scopedLog.Debug("Ignoring CIDR to identity mapping as it is shadowed by an endpoint IP")
   273  				// Skip calling back the listeners, since the endpoint IP has
   274  				// precedence over the new CIDR.
   275  				newIdentity.shadowed = true
   276  			}
   277  		}
   278  	} else if endpointIP := net.ParseIP(ip); endpointIP != nil { // Endpoint IP.
   279  		cidr = endpointIPToCIDR(endpointIP)
   280  
   281  		// Check whether the upserted endpoint IP will shadow that CIDR, and
   282  		// replace its mapping with the listeners if that was the case.
   283  		if !found {
   284  			cidrStr := cidr.String()
   285  			if cidrIdentity, cidrFound := ipc.ipToIdentityCache[cidrStr]; cidrFound {
   286  				oldHostIP, _ = ipc.getHostIPCache(cidrStr)
   287  				if cidrIdentity.ID != newIdentity.ID || !oldHostIP.Equal(hostIP) {
   288  					scopedLog.Debug("New endpoint IP started shadowing existing CIDR to identity mapping")
   289  					cidrIdentity.shadowed = true
   290  					ipc.ipToIdentityCache[cidrStr] = cidrIdentity
   291  					oldIdentity = &cidrIdentity.ID
   292  				} else {
   293  					// The endpoint IP and the CIDR are associated with the
   294  					// same identity and host IP. Nothing changes for the
   295  					// listeners.
   296  					callbackListeners = false
   297  				}
   298  			}
   299  		}
   300  	} else {
   301  		log.WithFields(logrus.Fields{
   302  			logfields.IPAddr:   ip,
   303  			logfields.Identity: newIdentity,
   304  			logfields.Key:      hostKey,
   305  		}).Error("Attempt to upsert invalid IP into ipcache layer")
   306  		return false
   307  	}
   308  
   309  	scopedLog.Debug("Upserting IP into ipcache layer")
   310  
   311  	// Update both maps.
   312  	ipc.ipToIdentityCache[ip] = newIdentity
   313  	// Delete the old identity, if any.
   314  	if found {
   315  		delete(ipc.identityToIPCache[cachedIdentity.ID], ip)
   316  		if len(ipc.identityToIPCache[cachedIdentity.ID]) == 0 {
   317  			delete(ipc.identityToIPCache, cachedIdentity.ID)
   318  		}
   319  	}
   320  	if _, ok := ipc.identityToIPCache[newIdentity.ID]; !ok {
   321  		ipc.identityToIPCache[newIdentity.ID] = map[string]struct{}{}
   322  	}
   323  	ipc.identityToIPCache[newIdentity.ID][ip] = struct{}{}
   324  
   325  	if hostIP == nil {
   326  		delete(ipc.ipToHostIPCache, ip)
   327  	} else {
   328  		ipc.ipToHostIPCache[ip] = IPKeyPair{IP: hostIP, Key: hostKey}
   329  	}
   330  
   331  	if callbackListeners && !newIdentity.shadowed {
   332  		for _, listener := range ipc.listeners {
   333  			listener.OnIPIdentityCacheChange(Upsert, *cidr, oldHostIP, hostIP, oldIdentity, newIdentity.ID, hostKey)
   334  		}
   335  	}
   336  
   337  	return true
   338  }
   339  
   340  // DumpToListenerLocked dumps the entire contents of the IPCache by triggering
   341  // the listener's "OnIPIdentityCacheChange" method for each entry in the cache.
   342  func (ipc *IPCache) DumpToListenerLocked(listener IPIdentityMappingListener) {
   343  	for ip, identity := range ipc.ipToIdentityCache {
   344  		if identity.shadowed {
   345  			continue
   346  		}
   347  		hostIP, encryptKey := ipc.getHostIPCache(ip)
   348  		_, cidr, err := net.ParseCIDR(ip)
   349  		if err != nil {
   350  			endpointIP := net.ParseIP(ip)
   351  			cidr = endpointIPToCIDR(endpointIP)
   352  		}
   353  		listener.OnIPIdentityCacheChange(Upsert, *cidr, nil, hostIP, nil, identity.ID, encryptKey)
   354  	}
   355  }
   356  
   357  // deleteLocked removes the provided IP-to-security-identity mapping
   358  // from ipc with the assumption that the IPCache's mutex is held.
   359  func (ipc *IPCache) deleteLocked(ip string, source source.Source) {
   360  	scopedLog := log.WithFields(logrus.Fields{
   361  		logfields.IPAddr: ip,
   362  	})
   363  
   364  	cachedIdentity, found := ipc.ipToIdentityCache[ip]
   365  	if !found {
   366  		scopedLog.Debug("Attempt to remove non-existing IP from ipcache layer")
   367  		return
   368  	}
   369  
   370  	if cachedIdentity.Source != source {
   371  		scopedLog.WithField("source", cachedIdentity.Source).
   372  			Debugf("Skipping delete of identity from source %s", source)
   373  		return
   374  	}
   375  
   376  	var cidr *net.IPNet
   377  	cacheModification := Delete
   378  	oldHostIP, encryptKey := ipc.getHostIPCache(ip)
   379  	var newHostIP net.IP
   380  	var oldIdentity *identity.NumericIdentity
   381  	newIdentity := cachedIdentity
   382  	callbackListeners := true
   383  
   384  	var err error
   385  	if _, cidr, err = net.ParseCIDR(ip); err == nil {
   386  		// Remove a reference for the prefix length if this is a CIDR.
   387  		pl, bits := cidr.Mask.Size()
   388  		switch bits {
   389  		case net.IPv6len * 8:
   390  			unrefPrefixLength(ipc.v6PrefixLengths, pl)
   391  		case net.IPv4len * 8:
   392  			unrefPrefixLength(ipc.v4PrefixLengths, pl)
   393  		}
   394  
   395  		// Check whether the deleted CIDR was shadowed by an endpoint IP. In
   396  		// this case, skip calling back the listeners since they don't know
   397  		// about its mapping.
   398  		if _, endpointIPFound := ipc.ipToIdentityCache[cidr.IP.String()]; endpointIPFound {
   399  			scopedLog.Debug("Deleting CIDR shadowed by endpoint IP")
   400  			callbackListeners = false
   401  		}
   402  	} else if endpointIP := net.ParseIP(ip); endpointIP != nil { // Endpoint IP.
   403  		// Convert the endpoint IP into an equivalent full CIDR.
   404  		bits := net.IPv6len * 8
   405  		if endpointIP.To4() != nil {
   406  			bits = net.IPv4len * 8
   407  		}
   408  		cidr = &net.IPNet{
   409  			IP:   endpointIP,
   410  			Mask: net.CIDRMask(bits, bits),
   411  		}
   412  
   413  		// Check whether the deleted endpoint IP was shadowing that CIDR, and
   414  		// restore its mapping with the listeners if that was the case.
   415  		cidrStr := cidr.String()
   416  		if cidrIdentity, cidrFound := ipc.ipToIdentityCache[cidrStr]; cidrFound {
   417  			newHostIP, _ = ipc.getHostIPCache(cidrStr)
   418  			if cidrIdentity.ID != cachedIdentity.ID || !oldHostIP.Equal(newHostIP) {
   419  				scopedLog.Debug("Removal of endpoint IP revives shadowed CIDR to identity mapping")
   420  				cacheModification = Upsert
   421  				cidrIdentity.shadowed = false
   422  				ipc.ipToIdentityCache[cidrStr] = cidrIdentity
   423  				oldIdentity = &cachedIdentity.ID
   424  				newIdentity = cidrIdentity
   425  			} else {
   426  				// The endpoint IP and the CIDR were associated with the same
   427  				// identity and host IP. Nothing changes for the listeners.
   428  				callbackListeners = false
   429  			}
   430  		}
   431  	} else {
   432  		scopedLog.Error("Attempt to delete invalid IP from ipcache layer")
   433  		return
   434  	}
   435  
   436  	scopedLog.Debug("Deleting IP from ipcache layer")
   437  
   438  	delete(ipc.ipToIdentityCache, ip)
   439  	delete(ipc.identityToIPCache[cachedIdentity.ID], ip)
   440  	if len(ipc.identityToIPCache[cachedIdentity.ID]) == 0 {
   441  		delete(ipc.identityToIPCache, cachedIdentity.ID)
   442  	}
   443  	delete(ipc.ipToHostIPCache, ip)
   444  
   445  	if callbackListeners {
   446  		for _, listener := range ipc.listeners {
   447  			listener.OnIPIdentityCacheChange(cacheModification, *cidr, oldHostIP, newHostIP,
   448  				oldIdentity, newIdentity.ID, encryptKey)
   449  		}
   450  	}
   451  }
   452  
   453  // Delete removes the provided IP-to-security-identity mapping from the IPCache.
   454  func (ipc *IPCache) Delete(IP string, source source.Source) {
   455  	ipc.mutex.Lock()
   456  	defer ipc.mutex.Unlock()
   457  	ipc.deleteLocked(IP, source)
   458  }
   459  
   460  // LookupByIP returns the corresponding security identity that endpoint IP maps
   461  // to within the provided IPCache, as well as if the corresponding entry exists
   462  // in the IPCache.
   463  func (ipc *IPCache) LookupByIP(IP string) (Identity, bool) {
   464  	ipc.mutex.RLock()
   465  	defer ipc.mutex.RUnlock()
   466  	return ipc.LookupByIPRLocked(IP)
   467  }
   468  
   469  // LookupByIPRLocked returns the corresponding security identity that endpoint IP maps
   470  // to within the provided IPCache, as well as if the corresponding entry exists
   471  // in the IPCache.
   472  func (ipc *IPCache) LookupByIPRLocked(IP string) (Identity, bool) {
   473  
   474  	identity, exists := ipc.ipToIdentityCache[IP]
   475  	return identity, exists
   476  }
   477  
   478  // LookupByPrefixRLocked looks for either the specified CIDR prefix, or if the
   479  // prefix is fully specified (ie, w.x.y.z/32 for IPv4), find the host for the
   480  // identity in the provided IPCache, and returns the corresponding security
   481  // identity as well as whether the entry exists in the IPCache.
   482  func (ipc *IPCache) LookupByPrefixRLocked(prefix string) (identity Identity, exists bool) {
   483  	if _, cidr, err := net.ParseCIDR(prefix); err == nil {
   484  		// If it's a fully specfied prefix, attempt to find the host
   485  		ones, bits := cidr.Mask.Size()
   486  		if ones == bits {
   487  			identity, exists = ipc.ipToIdentityCache[cidr.IP.String()]
   488  			if exists {
   489  				return
   490  			}
   491  		}
   492  	}
   493  	identity, exists = ipc.ipToIdentityCache[prefix]
   494  	return
   495  }
   496  
   497  // LookupByPrefix returns the corresponding security identity that endpoint IP
   498  // maps to within the provided IPCache, as well as if the corresponding entry
   499  // exists in the IPCache.
   500  func (ipc *IPCache) LookupByPrefix(IP string) (Identity, bool) {
   501  	ipc.mutex.RLock()
   502  	defer ipc.mutex.RUnlock()
   503  	return ipc.LookupByPrefixRLocked(IP)
   504  }
   505  
   506  // LookupByIdentity returns the set of IPs (endpoint or CIDR prefix) that have
   507  // security identity ID, as well as whether the corresponding entry exists in
   508  // the IPCache.
   509  func (ipc *IPCache) LookupByIdentity(id identity.NumericIdentity) (map[string]struct{}, bool) {
   510  	ipc.mutex.RLock()
   511  	defer ipc.mutex.RUnlock()
   512  	ips, exists := ipc.identityToIPCache[id]
   513  	return ips, exists
   514  }
   515  
   516  // GetIPIdentityMapModel returns all known endpoint IP to security identity mappings
   517  // stored in the key-value store.
   518  func GetIPIdentityMapModel() {
   519  	// TODO (ianvernon) return model of ip to identity mapping. For use in CLI.
   520  	// see GH-2555
   521  }