github.com/imran-kn/cilium-fork@v1.6.9/pkg/endpointmanager/conntrack.go (about)

     1  // Copyright 2016-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 endpointmanager
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"time"
    21  
    22  	"github.com/cilium/cilium/pkg/bpf"
    23  	"github.com/cilium/cilium/pkg/endpoint"
    24  	"github.com/cilium/cilium/pkg/logging/logfields"
    25  	"github.com/cilium/cilium/pkg/maps/ctmap"
    26  	"github.com/cilium/cilium/pkg/signal"
    27  	"github.com/sirupsen/logrus"
    28  )
    29  
    30  // runGC run CT's garbage collector for the given endpoint. `isLocal` refers if
    31  // the CT map is set to local. If `isIPv6` is set specifies that is the IPv6
    32  // map. `filter` represents the filter type to be used while looping all CT
    33  // entries.
    34  //
    35  // The provided endpoint is optional; if it is provided, then its map will be
    36  // garbage collected and any failures will be logged to the endpoint log.
    37  // Otherwise it will garbage-collect the global map and use the global log.
    38  func runGC(e *endpoint.Endpoint, ipv4, ipv6 bool, filter *ctmap.GCFilter) (mapType bpf.MapType, maxDeleteRatio float64) {
    39  	var maps []*ctmap.Map
    40  
    41  	if e == nil {
    42  		maps = ctmap.GlobalMaps(ipv4, ipv6)
    43  	} else {
    44  		maps = ctmap.LocalMaps(e, ipv4, ipv6)
    45  	}
    46  	for _, m := range maps {
    47  		path, err := m.Path()
    48  		if err == nil {
    49  			err = m.Open()
    50  		}
    51  		if err != nil {
    52  			msg := "Skipping CT garbage collection"
    53  			scopedLog := log.WithError(err).WithField(logfields.Path, path)
    54  			if os.IsNotExist(err) {
    55  				scopedLog.Debug(msg)
    56  			} else {
    57  				scopedLog.Warn(msg)
    58  			}
    59  			if e != nil {
    60  				e.LogStatus(endpoint.BPF, endpoint.Warning, fmt.Sprintf("%s: %s", msg, err))
    61  			}
    62  			continue
    63  		}
    64  		defer m.Close()
    65  
    66  		mapType = m.MapInfo.MapType
    67  
    68  		deleted := ctmap.GC(m, filter)
    69  
    70  		if deleted > 0 {
    71  			ratio := float64(deleted) / float64(m.MapInfo.MaxEntries)
    72  			if ratio > maxDeleteRatio {
    73  				maxDeleteRatio = ratio
    74  			}
    75  			log.WithFields(logrus.Fields{
    76  				logfields.Path: path,
    77  				"count":        deleted,
    78  			}).Debug("Deleted filtered entries from map")
    79  		}
    80  	}
    81  
    82  	return
    83  }
    84  
    85  func createGCFilter(initialScan bool, restoredEndpoints []*endpoint.Endpoint) *ctmap.GCFilter {
    86  	filter := &ctmap.GCFilter{
    87  		RemoveExpired: true,
    88  	}
    89  
    90  	// On the initial scan, scrub all IPs from the conntrack table which do
    91  	// not belong to IPs of any endpoint that has been restored. No new
    92  	// endpoints can appear yet so we can assume that any other entry not
    93  	// belonging to a restored endpoint has become stale.
    94  	if initialScan {
    95  		filter.ValidIPs = map[string]struct{}{}
    96  		for _, ep := range restoredEndpoints {
    97  			filter.ValidIPs[ep.IPv6.String()] = struct{}{}
    98  			filter.ValidIPs[ep.IPv4.String()] = struct{}{}
    99  		}
   100  	}
   101  
   102  	return filter
   103  }
   104  
   105  // EnableConntrackGC enables the connection tracking garbage collection.
   106  func EnableConntrackGC(ipv4, ipv6 bool, restoredEndpoints []*endpoint.Endpoint) {
   107  	var (
   108  		initialScan         = true
   109  		initialScanComplete = make(chan struct{})
   110  		mapType             bpf.MapType
   111  	)
   112  
   113  	go func() {
   114  		var wakeup = make(chan signal.SignalData)
   115  		ipv4Orig := ipv4
   116  		ipv6Orig := ipv6
   117  		for {
   118  			var maxDeleteRatio float64
   119  
   120  			eps := GetEndpoints()
   121  			if len(eps) > 0 || initialScan {
   122  				mapType, maxDeleteRatio = runGC(nil, ipv4, ipv6, createGCFilter(initialScan, restoredEndpoints))
   123  			}
   124  			for _, e := range eps {
   125  				if !e.ConntrackLocal() {
   126  					// Skip because GC was handled above.
   127  					continue
   128  				}
   129  				runGC(e, ipv4, ipv6, &ctmap.GCFilter{RemoveExpired: true})
   130  			}
   131  
   132  			if initialScan {
   133  				close(initialScanComplete)
   134  				initialScan = false
   135  
   136  				signal.RegisterChannel(signal.SignalNatFillUp, wakeup)
   137  				signal.SetupSignalListener()
   138  				signal.MuteChannel(signal.SignalNatFillUp)
   139  			}
   140  
   141  			signal.UnmuteChannel(signal.SignalNatFillUp)
   142  			select {
   143  			case x := <-wakeup:
   144  				ipv4 = false
   145  				ipv6 = false
   146  				if x == signal.SignalNatV4 {
   147  					ipv4 = true
   148  				} else if x == signal.SignalNatV6 {
   149  					ipv6 = true
   150  				}
   151  				// Drain current queue since we just woke up anyway.
   152  				for len(wakeup) > 0 {
   153  					x := <-wakeup
   154  					if x == signal.SignalNatV4 {
   155  						ipv4 = true
   156  					} else if x == signal.SignalNatV6 {
   157  						ipv6 = true
   158  					}
   159  				}
   160  			case <-time.After(ctmap.GetInterval(mapType, maxDeleteRatio)):
   161  				ipv4 = ipv4Orig
   162  				ipv6 = ipv6Orig
   163  			}
   164  			signal.MuteChannel(signal.SignalNatFillUp)
   165  		}
   166  	}()
   167  
   168  	select {
   169  	case <-initialScanComplete:
   170  		log.Info("Initial scan of connection tracking completed")
   171  	case <-time.After(30 * time.Second):
   172  		log.Fatal("Timeout while waiting for initial conntrack scan")
   173  	}
   174  }