github.phpd.cn/cilium/cilium@v1.6.12/daemon/ipam.go (about) 1 // Copyright 2016-2020 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 main 16 17 import ( 18 "fmt" 19 "net" 20 "strings" 21 "time" 22 23 "github.com/cilium/cilium/api/v1/models" 24 ipamapi "github.com/cilium/cilium/api/v1/server/restapi/ipam" 25 "github.com/cilium/cilium/pkg/api" 26 "github.com/cilium/cilium/pkg/cidr" 27 "github.com/cilium/cilium/pkg/datapath" 28 "github.com/cilium/cilium/pkg/defaults" 29 "github.com/cilium/cilium/pkg/endpointmanager" 30 "github.com/cilium/cilium/pkg/ipam" 31 "github.com/cilium/cilium/pkg/logging/logfields" 32 "github.com/cilium/cilium/pkg/node" 33 "github.com/cilium/cilium/pkg/option" 34 35 "github.com/go-openapi/runtime/middleware" 36 "github.com/go-openapi/swag" 37 ) 38 39 type postIPAM struct { 40 daemon *Daemon 41 } 42 43 // NewPostIPAMHandler creates a new postIPAM from the daemon. 44 func NewPostIPAMHandler(d *Daemon) ipamapi.PostIPAMHandler { 45 return &postIPAM{daemon: d} 46 } 47 48 // Handle incoming requests address allocation requests for the daemon. 49 func (h *postIPAM) Handle(params ipamapi.PostIPAMParams) middleware.Responder { 50 family := strings.ToLower(swag.StringValue(params.Family)) 51 owner := swag.StringValue(params.Owner) 52 var expirationTimeout time.Duration 53 if swag.BoolValue(params.Expiration) { 54 expirationTimeout = defaults.IPAMExpiration 55 } 56 ipv4Result, ipv6Result, err := h.daemon.ipam.AllocateNextWithExpiration(family, owner, expirationTimeout) 57 if err != nil { 58 return api.Error(ipamapi.PostIPAMFailureCode, err) 59 } 60 61 resp := &models.IPAMResponse{ 62 HostAddressing: node.GetNodeAddressing(), 63 Address: &models.AddressPair{}, 64 } 65 66 if ipv4Result != nil { 67 resp.Address.IPV4 = ipv4Result.IP.String() 68 resp.IPV4 = &models.IPAMAddressResponse{ 69 Cidrs: ipv4Result.CIDRs, 70 IP: ipv4Result.IP.String(), 71 MasterMac: ipv4Result.Master, 72 Gateway: ipv4Result.GatewayIP, 73 ExpirationUUID: ipv4Result.ExpirationUUID, 74 } 75 } 76 77 if ipv6Result != nil { 78 resp.Address.IPV6 = ipv6Result.IP.String() 79 resp.IPV6 = &models.IPAMAddressResponse{ 80 Cidrs: ipv6Result.CIDRs, 81 IP: ipv6Result.IP.String(), 82 MasterMac: ipv6Result.Master, 83 Gateway: ipv6Result.GatewayIP, 84 ExpirationUUID: ipv6Result.ExpirationUUID, 85 } 86 } 87 88 return ipamapi.NewPostIPAMCreated().WithPayload(resp) 89 } 90 91 type postIPAMIP struct { 92 daemon *Daemon 93 } 94 95 // NewPostIPAMIPHandler creates a new postIPAM from the daemon. 96 func NewPostIPAMIPHandler(d *Daemon) ipamapi.PostIPAMIPHandler { 97 return &postIPAMIP{ 98 daemon: d, 99 } 100 } 101 102 // Handle incoming requests address allocation requests for the daemon. 103 func (h *postIPAMIP) Handle(params ipamapi.PostIPAMIPParams) middleware.Responder { 104 owner := swag.StringValue(params.Owner) 105 if err := h.daemon.ipam.AllocateIPString(params.IP, owner); err != nil { 106 return api.Error(ipamapi.PostIPAMIPFailureCode, err) 107 } 108 109 return ipamapi.NewPostIPAMIPOK() 110 } 111 112 type deleteIPAMIP struct { 113 daemon *Daemon 114 } 115 116 // NewDeleteIPAMIPHandler handle incoming requests to delete addresses. 117 func NewDeleteIPAMIPHandler(d *Daemon) ipamapi.DeleteIPAMIPHandler { 118 return &deleteIPAMIP{daemon: d} 119 } 120 121 func (h *deleteIPAMIP) Handle(params ipamapi.DeleteIPAMIPParams) middleware.Responder { 122 // Release of an IP that is in use is not allowed 123 if ep := endpointmanager.LookupIPv4(params.IP); ep != nil { 124 return api.Error(ipamapi.DeleteIPAMIPFailureCode, fmt.Errorf("IP is in use by endpoint %d", ep.ID)) 125 } 126 if ep := endpointmanager.LookupIPv6(params.IP); ep != nil { 127 return api.Error(ipamapi.DeleteIPAMIPFailureCode, fmt.Errorf("IP is in use by endpoint %d", ep.ID)) 128 } 129 130 if err := h.daemon.ipam.ReleaseIPString(params.IP); err != nil { 131 return api.Error(ipamapi.DeleteIPAMIPFailureCode, err) 132 } 133 134 return ipamapi.NewDeleteIPAMIPOK() 135 } 136 137 // DumpIPAM dumps in the form of a map, the list of 138 // reserved IPv4 and IPv6 addresses. 139 func (d *Daemon) DumpIPAM() *models.IPAMStatus { 140 allocv4, allocv6, st := d.ipam.Dump() 141 status := &models.IPAMStatus{ 142 Status: st, 143 } 144 145 v4 := []string{} 146 for ip := range allocv4 { 147 v4 = append(v4, ip) 148 } 149 150 v6 := []string{} 151 if allocv4 == nil { 152 allocv4 = map[string]string{} 153 } 154 for ip, owner := range allocv6 { 155 v6 = append(v6, ip) 156 // merge allocv6 into allocv4 157 allocv4[ip] = owner 158 } 159 160 if option.Config.EnableIPv4 { 161 status.IPV4 = v4 162 } 163 164 if option.Config.EnableIPv6 { 165 status.IPV6 = v6 166 } 167 168 status.Allocations = allocv4 169 170 return status 171 } 172 173 func (d *Daemon) allocateDatapathIPs(family datapath.NodeAddressingFamily) (routerIP net.IP, err error) { 174 // Blacklist allocation of the external IP 175 d.ipam.BlacklistIP(family.PrimaryExternal(), "node-ip") 176 177 // (Re-)allocate the router IP. If not possible, allocate a fresh IP. 178 // In that case, removal and re-creation of the cilium_host is 179 // required. It will also cause disruption of networking until all 180 // endpoints have been regenerated. 181 routerIP = family.Router() 182 if routerIP != nil { 183 err = d.ipam.AllocateIP(routerIP, "router") 184 if err != nil { 185 log.Warningf("Router IP could not be re-allocated. Need to re-allocate. This will cause brief network disruption") 186 187 // The restored router IP is not part of the allocation range. 188 // This indicates that the allocation range has changed. 189 if !option.Config.IsFlannelMasterDeviceSet() { 190 deleteHostDevice() 191 } 192 193 // force re-allocation of the router IP 194 routerIP = nil 195 } 196 } 197 198 if routerIP == nil { 199 var result *ipam.AllocationResult 200 result, err = d.ipam.AllocateNextFamily(ipam.DeriveFamily(family.PrimaryExternal()), "router") 201 if err != nil { 202 err = fmt.Errorf("Unable to allocate IPv4 router IP: %s", err) 203 return 204 } 205 routerIP = result.IP 206 } 207 208 return 209 } 210 211 func (d *Daemon) allocateHealthIPs() error { 212 bootstrapStats.healthCheck.Start() 213 if option.Config.EnableHealthChecking && option.Config.EnableEndpointHealthChecking { 214 if option.Config.EnableIPv4 { 215 result, err := d.ipam.AllocateNextFamily(ipam.IPv4, "health") 216 if err != nil { 217 return fmt.Errorf("unable to allocate health IPs: %s,see https://cilium.link/ipam-range-full", err) 218 } 219 220 d.nodeDiscovery.LocalNode.IPv4HealthIP = result.IP 221 log.Debugf("IPv4 health endpoint address: %s", result.IP) 222 } 223 224 if option.Config.EnableIPv6 { 225 result, err := d.ipam.AllocateNextFamily(ipam.IPv6, "health") 226 if err != nil { 227 if d.nodeDiscovery.LocalNode.IPv4HealthIP != nil { 228 d.ipam.ReleaseIP(d.nodeDiscovery.LocalNode.IPv4HealthIP) 229 } 230 return fmt.Errorf("unable to allocate health IPs: %s,see https://cilium.link/ipam-range-full", err) 231 } 232 233 d.nodeDiscovery.LocalNode.IPv6HealthIP = result.IP 234 log.Debugf("IPv6 health endpoint address: %s", result.IP) 235 } 236 } 237 bootstrapStats.healthCheck.End(true) 238 return nil 239 } 240 241 func (d *Daemon) allocateIPs() error { 242 bootstrapStats.ipam.Start() 243 if option.Config.EnableIPv4 { 244 routerIP, err := d.allocateDatapathIPs(d.datapath.LocalNodeAddressing().IPv4()) 245 if err != nil { 246 return err 247 } 248 if routerIP != nil { 249 node.SetInternalIPv4(routerIP) 250 } 251 } 252 253 if option.Config.EnableIPv6 { 254 routerIP, err := d.allocateDatapathIPs(d.datapath.LocalNodeAddressing().IPv6()) 255 if err != nil { 256 return err 257 } 258 if routerIP != nil { 259 node.SetIPv6Router(routerIP) 260 } 261 } 262 263 log.Info("Addressing information:") 264 log.Infof(" Cluster-Name: %s", option.Config.ClusterName) 265 log.Infof(" Cluster-ID: %d", option.Config.ClusterID) 266 log.Infof(" Local node-name: %s", node.GetName()) 267 log.Infof(" Node-IPv6: %s", node.GetIPv6()) 268 269 if option.Config.EnableIPv6 { 270 log.Infof(" IPv6 node prefix: %s", node.GetIPv6NodeRange()) 271 log.Infof(" IPv6 allocation prefix: %s", node.GetIPv6AllocRange()) 272 log.Infof(" IPv6 router address: %s", node.GetIPv6Router()) 273 274 if addrs, err := d.datapath.LocalNodeAddressing().IPv6().LocalAddresses(); err != nil { 275 log.WithError(err).Fatal("Unable to list local IPv6 addresses") 276 } else { 277 log.Info(" Local IPv6 addresses:") 278 for _, ip := range addrs { 279 log.Infof(" - %s", ip) 280 } 281 } 282 } 283 284 log.Infof(" External-Node IPv4: %s", node.GetExternalIPv4()) 285 log.Infof(" Internal-Node IPv4: %s", node.GetInternalIPv4()) 286 287 if option.Config.EnableIPv4 { 288 log.Infof(" Cluster IPv4 prefix: %s", node.GetIPv4ClusterRange()) 289 log.Infof(" IPv4 allocation prefix: %s", node.GetIPv4AllocRange()) 290 291 if c := option.Config.IPv4NativeRoutingCIDR(); c != nil { 292 log.Infof(" IPv4 native routing prefix: %s", c.String()) 293 } 294 295 // Allocate IPv4 service loopback IP 296 loopbackIPv4 := net.ParseIP(option.Config.LoopbackIPv4) 297 if loopbackIPv4 == nil { 298 return fmt.Errorf("Invalid IPv4 loopback address %s", option.Config.LoopbackIPv4) 299 } 300 node.SetIPv4Loopback(loopbackIPv4) 301 log.Infof(" Loopback IPv4: %s", node.GetIPv4Loopback().String()) 302 303 if addrs, err := d.datapath.LocalNodeAddressing().IPv4().LocalAddresses(); err != nil { 304 log.WithError(err).Fatal("Unable to list local IPv4 addresses") 305 } else { 306 log.Info(" Local IPv4 addresses:") 307 for _, ip := range addrs { 308 log.Infof(" - %s", ip) 309 } 310 } 311 } 312 313 bootstrapStats.ipam.End(true) 314 return d.allocateHealthIPs() 315 } 316 317 func (d *Daemon) bootstrapIPAM() { 318 // If the device has been specified, the IPv4AllocPrefix and the 319 // IPv6AllocPrefix were already allocated before the k8s.Init(). 320 // 321 // If the device hasn't been specified, k8s.Init() allocated the 322 // IPv4AllocPrefix and the IPv6AllocPrefix from k8s node annotations. 323 // 324 // If k8s.Init() failed to retrieve the IPv4AllocPrefix we can try to derive 325 // it from an existing node_config.h file or from previous cilium_host 326 // interfaces. 327 // 328 // Then, we will calculate the IPv4 or IPv6 alloc prefix based on the IPv6 329 // or IPv4 alloc prefix, respectively, retrieved by k8s node annotations. 330 bootstrapStats.ipam.Start() 331 log.Info("Initializing node addressing") 332 333 node.SetIPv4ClusterCidrMaskSize(option.Config.IPv4ClusterCIDRMaskSize) 334 335 if option.Config.IPv4Range != AutoCIDR { 336 allocCIDR, err := cidr.ParseCIDR(option.Config.IPv4Range) 337 if err != nil { 338 log.WithError(err).WithField(logfields.V4Prefix, option.Config.IPv4Range).Fatal("Invalid IPv4 allocation prefix") 339 } 340 node.SetIPv4AllocRange(allocCIDR) 341 } 342 343 if option.Config.IPv6Range != AutoCIDR { 344 _, net, err := net.ParseCIDR(option.Config.IPv6Range) 345 if err != nil { 346 log.WithError(err).WithField(logfields.V6Prefix, option.Config.IPv6Range).Fatal("Invalid IPv6 allocation prefix") 347 } 348 349 if err := node.SetIPv6NodeRange(net); err != nil { 350 log.WithError(err).WithField(logfields.V6Prefix, net).Fatal("Invalid per node IPv6 allocation prefix") 351 } 352 } 353 354 if err := node.AutoComplete(); err != nil { 355 log.WithError(err).Fatal("Cannot autocomplete node addresses") 356 } 357 358 // Set up ipam conf after init() because we might be running d.conf.KVStoreIPv4Registration 359 d.ipam = ipam.NewIPAM(d.datapath.LocalNodeAddressing(), ipam.Configuration{ 360 EnableIPv4: option.Config.EnableIPv4, 361 EnableIPv6: option.Config.EnableIPv6, 362 }, d) 363 bootstrapStats.ipam.End(true) 364 }