github.com/elfadel/cilium@v1.6.12/pkg/fqdn/name_manager.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 fqdn
    16  
    17  import (
    18  	"context"
    19  	"net"
    20  	"regexp"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/cilium/cilium/pkg/identity"
    25  	"github.com/cilium/cilium/pkg/ipcache"
    26  	"github.com/cilium/cilium/pkg/lock"
    27  	"github.com/cilium/cilium/pkg/logging/logfields"
    28  	"github.com/cilium/cilium/pkg/policy/api"
    29  
    30  	"github.com/sirupsen/logrus"
    31  )
    32  
    33  // NameManager maintains state DNS names, via FQDNSelector or exact match for
    34  // polling, need to be tracked. It is the main structure which relates the FQDN
    35  // subsystem to the policy subsystem for plumbing the relation between a DNS
    36  // name and the corresponding IPs which have been returned via DNS lookups.
    37  // When DNS updates are given to a NameManager it update cached selectors as
    38  // required via UpdateSelectors.
    39  // DNS information is cached, respecting TTL.
    40  type NameManager struct {
    41  	lock.Mutex
    42  
    43  	// config is a copy from when this instance was initialized.
    44  	// It is read-only once set
    45  	config Config
    46  
    47  	// namesToPoll is the set of names that need to be polled. These do not
    48  	// include regexes, as those are not polled directly.
    49  	namesToPoll map[string]struct{}
    50  
    51  	// allSelectors contains all FQDNSelectors which are present in all policy. We
    52  	// use these selectors to map selectors --> IPs.
    53  	allSelectors map[api.FQDNSelector]*regexp.Regexp
    54  
    55  	// cache is a private copy of the pointer from config.
    56  	cache *DNSCache
    57  
    58  	bootstrapCompleted bool
    59  }
    60  
    61  // Lock must be held during any calls to RegisterForIdentityUpdatesLocked or
    62  // UnregisterForIdentityUpdatesLocked.
    63  func (n *NameManager) Lock() {
    64  	n.Mutex.Lock()
    65  }
    66  
    67  // Unlock must be called after calls to RegisterForIdentityUpdatesLocked or
    68  // UnregisterForIdentityUpdatesLocked are done.
    69  func (n *NameManager) Unlock() {
    70  	n.Mutex.Unlock()
    71  }
    72  
    73  // RegisterForIdentityUpdatesLocked exposes this FQDNSelector so that identities
    74  // for IPs contained in a DNS response that matches said selector can be
    75  // propagated back to the SelectorCache via `UpdateFQDNSelector`. All DNS names
    76  // contained within the NameManager's cache are iterated over to see if they match
    77  // the FQDNSelector. All IPs which correspond to the DNS names which match this
    78  // Selector will be returned as CIDR identities, as other DNS Names which have
    79  // already been resolved may match this FQDNSelector.
    80  func (n *NameManager) RegisterForIdentityUpdatesLocked(selector api.FQDNSelector) []identity.NumericIdentity {
    81  	_, exists := n.allSelectors[selector]
    82  	if exists {
    83  		log.WithField("fqdnSelector", selector).Warning("FQDNSelector was already registered for updates, returning without any identities")
    84  		return nil
    85  	}
    86  
    87  	// This error should never occur since the FQDNSelector has already been
    88  	// validated, but account for it for good measure.
    89  	regex, err := selector.ToRegex()
    90  	if err != nil {
    91  		log.WithError(err).WithField("fqdnSelector", selector).Error("FQDNSelector did not compile to valid regex")
    92  		return nil
    93  	}
    94  
    95  	// Update names to poll for DNS poller since we now care about this selector.
    96  	if len(selector.MatchName) > 0 {
    97  		n.namesToPoll[prepareMatchName(selector.MatchName)] = struct{}{}
    98  	}
    99  
   100  	n.allSelectors[selector] = regex
   101  	_, selectorIPMapping := mapSelectorsToIPs(map[api.FQDNSelector]struct{}{selector: {}}, n.cache)
   102  
   103  	// Allocate identities for each IPNet and then map to selector
   104  	selectorIPs := selectorIPMapping[selector]
   105  	log.WithFields(logrus.Fields{
   106  		"fqdnSelector": selector,
   107  		"ips":          selectorIPs,
   108  	}).Debug("getting identities for IPs associated with FQDNSelector")
   109  	var currentlyAllocatedIdentities []*identity.Identity
   110  	if currentlyAllocatedIdentities, err = ipcache.AllocateCIDRsForIPs(selectorIPs); err != nil {
   111  		log.WithError(err).WithField("prefixes", selectorIPs).Warn(
   112  			"failed to allocate identities for IPs")
   113  		return nil
   114  	}
   115  	numIDs := make([]identity.NumericIdentity, 0, len(currentlyAllocatedIdentities))
   116  	for i := range currentlyAllocatedIdentities {
   117  		numIDs = append(numIDs, currentlyAllocatedIdentities[i].ID)
   118  	}
   119  
   120  	return numIDs
   121  }
   122  
   123  // UnregisterForIdentityUpdatesLocked removes this FQDNSelector from the set of
   124  // FQDNSelectors which are being tracked by the NameManager. No more updates for IPs
   125  // which correspond to said selector are propagated.
   126  func (n *NameManager) UnregisterForIdentityUpdatesLocked(selector api.FQDNSelector) {
   127  	delete(n.allSelectors, selector)
   128  	if len(selector.MatchName) > 0 {
   129  		delete(n.namesToPoll, prepareMatchName(selector.MatchName))
   130  	}
   131  }
   132  
   133  // NewNameManager creates an initialized NameManager.
   134  // When config.Cache is nil, the global fqdn.DefaultDNSCache is used.
   135  func NewNameManager(config Config) *NameManager {
   136  
   137  	if config.Cache == nil {
   138  		config.Cache = NewDNSCache(0)
   139  	}
   140  
   141  	if config.UpdateSelectors == nil {
   142  		config.UpdateSelectors = func(ctx context.Context, selectorIPMapping map[api.FQDNSelector][]net.IP, namesMissingIPs []api.FQDNSelector) (*sync.WaitGroup, error) {
   143  			return &sync.WaitGroup{}, nil
   144  		}
   145  	}
   146  
   147  	return &NameManager{
   148  		config:       config,
   149  		namesToPoll:  make(map[string]struct{}),
   150  		allSelectors: make(map[api.FQDNSelector]*regexp.Regexp),
   151  		cache:        config.Cache,
   152  	}
   153  
   154  }
   155  
   156  // GetDNSCache returns the DNSCache used by the NameManager
   157  func (n *NameManager) GetDNSCache() *DNSCache {
   158  	return n.cache
   159  }
   160  
   161  // GetDNSNames returns a snapshot of the DNS names managed by this NameManager
   162  func (n *NameManager) GetDNSNames() (dnsNames []string) {
   163  	n.Lock()
   164  	defer n.Unlock()
   165  
   166  	for name := range n.namesToPoll {
   167  		dnsNames = append(dnsNames, name)
   168  	}
   169  
   170  	return dnsNames
   171  }
   172  
   173  // UpdateGenerateDNS inserts the new DNS information into the cache. If the IPs
   174  // have changed for a name, store which rules must be updated in rulesToUpdate,
   175  // regenerate them, and emit via UpdateSelectors.
   176  func (n *NameManager) UpdateGenerateDNS(ctx context.Context, lookupTime time.Time, updatedDNSIPs map[string]*DNSIPRecords) (wg *sync.WaitGroup, err error) {
   177  	n.Mutex.Lock()
   178  	defer n.Mutex.Unlock()
   179  
   180  	// Update IPs in n
   181  	fqdnSelectorsToUpdate, updatedDNSNames := n.updateDNSIPs(lookupTime, updatedDNSIPs)
   182  	for dnsName, IPs := range updatedDNSNames {
   183  		log.WithFields(logrus.Fields{
   184  			"matchName":             dnsName,
   185  			"IPs":                   IPs,
   186  			"fqdnSelectorsToUpdate": fqdnSelectorsToUpdate,
   187  		}).Debug("Updated FQDN with new IPs")
   188  	}
   189  
   190  	namesMissingIPs, selectorIPMapping := n.generateSelectorUpdates(fqdnSelectorsToUpdate)
   191  	if len(namesMissingIPs) != 0 {
   192  		log.WithField(logfields.DNSName, namesMissingIPs).
   193  			Debug("No IPs to insert when generating DNS name selected by ToFQDN rule")
   194  	}
   195  
   196  	return n.config.UpdateSelectors(ctx, selectorIPMapping, namesMissingIPs)
   197  }
   198  
   199  // ForceGenerateDNS unconditionally regenerates all rules that refer to DNS
   200  // names in namesToRegen. These names are FQDNs and toFQDNs.matchPatterns or
   201  // matchNames that match them will cause these rules to regenerate.
   202  func (n *NameManager) ForceGenerateDNS(ctx context.Context, namesToRegen []string) (wg *sync.WaitGroup, err error) {
   203  	n.Mutex.Lock()
   204  	defer n.Mutex.Unlock()
   205  
   206  	affectedFQDNSels := make(map[api.FQDNSelector]struct{}, 0)
   207  	for _, dnsName := range namesToRegen {
   208  		for fqdnSel, fqdnRegEx := range n.allSelectors {
   209  			if fqdnRegEx.MatchString(dnsName) {
   210  				affectedFQDNSels[fqdnSel] = struct{}{}
   211  			}
   212  		}
   213  	}
   214  
   215  	namesMissingIPs, selectorIPMapping := mapSelectorsToIPs(affectedFQDNSels, n.cache)
   216  	if len(namesMissingIPs) != 0 {
   217  		log.WithField(logfields.DNSName, namesMissingIPs).
   218  			Debug("No IPs to insert when generating DNS name selected by ToFQDN rule")
   219  	}
   220  
   221  	// emit the new rules
   222  	return n.config.
   223  		UpdateSelectors(ctx, selectorIPMapping, namesMissingIPs)
   224  }
   225  
   226  func (n *NameManager) CompleteBootstrap() {
   227  	n.Lock()
   228  	n.bootstrapCompleted = true
   229  	n.Unlock()
   230  }
   231  
   232  // updateDNSIPs updates the IPs for each DNS name in updatedDNSIPs.
   233  // It returns:
   234  // affectedSelectors: a set of all FQDNSelectors which match DNS Names whose
   235  // corresponding set of IPs has changed.
   236  // updatedNames: a map of DNS names to all the valid IPs we store for each.
   237  func (n *NameManager) updateDNSIPs(lookupTime time.Time, updatedDNSIPs map[string]*DNSIPRecords) (affectedSelectors map[api.FQDNSelector]struct{}, updatedNames map[string][]net.IP) {
   238  	updatedNames = make(map[string][]net.IP, len(updatedDNSIPs))
   239  	affectedSelectors = make(map[api.FQDNSelector]struct{}, len(updatedDNSIPs))
   240  
   241  perDNSName:
   242  	for dnsName, lookupIPs := range updatedDNSIPs {
   243  		updated := n.updateIPsForName(lookupTime, dnsName, lookupIPs.IPs, lookupIPs.TTL)
   244  
   245  		// The IPs didn't change. No more to be done for this dnsName
   246  		if !updated && n.bootstrapCompleted {
   247  			log.WithFields(logrus.Fields{
   248  				"dnsName":   dnsName,
   249  				"lookupIPs": lookupIPs,
   250  			}).Debug("FQDN: IPs didn't change for DNS name")
   251  			continue perDNSName
   252  		}
   253  
   254  		// record the IPs that were different
   255  		updatedNames[dnsName] = lookupIPs.IPs
   256  
   257  		// accumulate the new selectors affected by new IPs
   258  		if len(n.allSelectors) == 0 {
   259  			log.WithFields(logrus.Fields{
   260  				"dnsName":   dnsName,
   261  				"lookupIPs": lookupIPs,
   262  			}).Debug("FQDN: No selectors registered for updates")
   263  		}
   264  		for fqdnSel, fqdnRegex := range n.allSelectors {
   265  			matches := fqdnRegex.MatchString(dnsName)
   266  			if matches {
   267  				affectedSelectors[fqdnSel] = struct{}{}
   268  			}
   269  		}
   270  	}
   271  
   272  	return affectedSelectors, updatedNames
   273  }
   274  
   275  // generateSelectorUpdates iterates over all names in the DNS cache managed by
   276  // gen and figures out to which FQDNSelectors managed by the cache these names
   277  // map. Returns the set of FQDNSelectors which map to no IPs, and a mapping
   278  // of FQDNSelectors to IPs.
   279  func (n *NameManager) generateSelectorUpdates(fqdnSelectors map[api.FQDNSelector]struct{}) (namesMissingIPs []api.FQDNSelector, selectorIPMapping map[api.FQDNSelector][]net.IP) {
   280  	return mapSelectorsToIPs(fqdnSelectors, n.cache)
   281  }
   282  
   283  // updateIPsName will update the IPs for dnsName. It always retains a copy of
   284  // newIPs.
   285  // updated is true when the new IPs differ from the old IPs
   286  func (n *NameManager) updateIPsForName(lookupTime time.Time, dnsName string, newIPs []net.IP, ttl int) (updated bool) {
   287  	cacheIPs := n.cache.Lookup(dnsName)
   288  
   289  	if n.config.MinTTL > ttl {
   290  		ttl = n.config.MinTTL
   291  	}
   292  
   293  	n.cache.Update(lookupTime, dnsName, newIPs, ttl)
   294  	sortedNewIPs := n.cache.Lookup(dnsName) // DNSCache returns IPs sorted
   295  
   296  	// The 0 checks below account for an unlike race condition where this
   297  	// function is called with already expired data and if other cache data
   298  	// from before also expired.
   299  	return (len(cacheIPs) == 0 && len(sortedNewIPs) == 0) || !sortedIPsAreEqual(sortedNewIPs, cacheIPs)
   300  }