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 }