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

     1  // Copyright 2017-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 ipam
    16  
    17  import (
    18  	"net"
    19  	"strings"
    20  
    21  	"github.com/cilium/cilium/pkg/datapath"
    22  	"github.com/cilium/cilium/pkg/defaults"
    23  	"github.com/cilium/cilium/pkg/logging"
    24  	"github.com/cilium/cilium/pkg/logging/logfields"
    25  	"github.com/cilium/cilium/pkg/option"
    26  
    27  	"github.com/sirupsen/logrus"
    28  	"github.com/vishvananda/netlink"
    29  )
    30  
    31  var (
    32  	log = logging.DefaultLogger.WithField(logfields.LogSubsys, "ipam")
    33  )
    34  
    35  type ErrAllocation error
    36  
    37  // Family is the type describing all address families support by the IP
    38  // allocation manager
    39  type Family string
    40  
    41  const (
    42  	IPv6 Family = "ipv6"
    43  	IPv4 Family = "ipv4"
    44  )
    45  
    46  // DeriveFamily derives the address family of an IP
    47  func DeriveFamily(ip net.IP) Family {
    48  	if ip.To4() == nil {
    49  		return IPv6
    50  	}
    51  	return IPv4
    52  }
    53  
    54  // Configuration is the configuration of an IP address manager
    55  type Configuration struct {
    56  	EnableIPv4 bool
    57  	EnableIPv6 bool
    58  }
    59  
    60  // Owner is the interface the owner of an IPAM allocator has to implement
    61  type Owner interface {
    62  	// K8sEventReceived is called to do metrics accounting for received
    63  	// Kubernetes events
    64  	K8sEventReceived(scope string, action string, valid, equal bool)
    65  
    66  	// K8sEventProcessed is called to do metrics accounting for each processed
    67  	// Kubernetes event
    68  	K8sEventProcessed(scope string, action string, status bool)
    69  
    70  	// UpdateCiliumNodeResource is called to create/update the CiliumNode
    71  	// resource. The function must block until the custom resource has been
    72  	// created.
    73  	UpdateCiliumNodeResource()
    74  }
    75  
    76  // NewIPAM returns a new IP address manager
    77  func NewIPAM(nodeAddressing datapath.NodeAddressing, c Configuration, owner Owner) *IPAM {
    78  	ipam := &IPAM{
    79  		nodeAddressing:   nodeAddressing,
    80  		config:           c,
    81  		owner:            map[string]string{},
    82  		expirationTimers: map[string]string{},
    83  		blacklist: IPBlacklist{
    84  			ips: map[string]string{},
    85  		},
    86  	}
    87  
    88  	switch strings.ToLower(option.Config.IPAM) {
    89  	case "":
    90  		log.WithFields(logrus.Fields{
    91  			logfields.V4Prefix: nodeAddressing.IPv4().AllocationCIDR(),
    92  			logfields.V6Prefix: nodeAddressing.IPv6().AllocationCIDR(),
    93  		}).Info("Initializing hostscope IPAM")
    94  
    95  		if c.EnableIPv6 {
    96  			ipam.IPv6Allocator = newHostScopeAllocator(nodeAddressing.IPv6().AllocationCIDR().IPNet)
    97  		}
    98  
    99  		if c.EnableIPv4 {
   100  			ipam.IPv4Allocator = newHostScopeAllocator(nodeAddressing.IPv4().AllocationCIDR().IPNet)
   101  		}
   102  	case option.IPAMCRD, option.IPAMENI:
   103  		log.Info("Initializing CRD-based IPAM")
   104  		if c.EnableIPv6 {
   105  			ipam.IPv6Allocator = newCRDAllocator(IPv6, owner)
   106  		}
   107  
   108  		if c.EnableIPv4 {
   109  			ipam.IPv4Allocator = newCRDAllocator(IPv4, owner)
   110  		}
   111  	default:
   112  		log.Fatalf("Unknown IPAM backend %s", option.Config.IPAM)
   113  	}
   114  
   115  	return ipam
   116  }
   117  
   118  func nextIP(ip net.IP) {
   119  	for j := len(ip) - 1; j >= 0; j-- {
   120  		ip[j]++
   121  		if ip[j] > 0 {
   122  			break
   123  		}
   124  	}
   125  }
   126  
   127  func (ipam *IPAM) reserveLocalRoutes() {
   128  	log.Debug("Checking local routes for conflicts...")
   129  
   130  	link, err := netlink.LinkByName(defaults.HostDevice)
   131  	if err != nil || link == nil {
   132  		log.WithError(err).Warnf("Unable to find net_device %s", defaults.HostDevice)
   133  		return
   134  	}
   135  
   136  	routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
   137  	if err != nil {
   138  		log.WithError(err).Warn("Unable to retrieve local routes")
   139  		return
   140  	}
   141  
   142  	for _, r := range routes {
   143  		// ignore routes which point to defaults.HostDevice
   144  		if r.LinkIndex == link.Attrs().Index {
   145  			log.WithField("route", r).Debugf("Ignoring route: points to %s", defaults.HostDevice)
   146  			continue
   147  		}
   148  
   149  		if r.Dst == nil {
   150  			log.WithField("route", r).Debug("Ignoring route: no destination address")
   151  			continue
   152  		}
   153  
   154  		// ignore black hole route
   155  		if r.Src == nil && r.Gw == nil {
   156  			log.WithField("route", r).Debugf("Ignoring route: black hole")
   157  			continue
   158  		}
   159  
   160  		log.WithField("route", r.Dst).Info("Blacklisting local route as no-alloc")
   161  		ipam.BlacklistIPNet(*r.Dst, "local route: "+r.Dst.String())
   162  	}
   163  }
   164  
   165  // ReserveLocalRoutes walks through local routes/subnets and reserves them in
   166  // the allocator pool in case of overlap
   167  func (ipam *IPAM) ReserveLocalRoutes() {
   168  	if !option.Config.BlacklistConflictingRoutes {
   169  		return
   170  	}
   171  
   172  	if ipam.IPv4Allocator != nil {
   173  		ipam.reserveLocalRoutes()
   174  	}
   175  }
   176  
   177  // BlacklistIP ensures that a certain IP is never allocated. It is preferred to
   178  // use BlacklistIP() instead of allocating the IP as the allocation block can
   179  // change and suddenly cover the IP to be blacklisted.
   180  func (ipam *IPAM) BlacklistIP(ip net.IP, owner string) {
   181  	ipam.allocatorMutex.Lock()
   182  	ipam.blacklist.ips[ip.String()] = owner
   183  	ipam.allocatorMutex.Unlock()
   184  }
   185  
   186  // BlacklistIPNet ensures that a certain IPNetwork is never allocated, similar
   187  // to BlacklistIP.
   188  func (ipam *IPAM) BlacklistIPNet(ipNet net.IPNet, owner string) {
   189  	ipam.allocatorMutex.Lock()
   190  	ipam.blacklist.ipNets = append(ipam.blacklist.ipNets, &IPNetWithOwner{
   191  		ipNet: ipNet,
   192  		owner: owner,
   193  	})
   194  	ipam.allocatorMutex.Unlock()
   195  }