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  }