github.com/cilium/cilium@v1.16.2/pkg/node/address.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package node 5 6 import ( 7 "bufio" 8 "context" 9 "fmt" 10 "net" 11 "os" 12 "strconv" 13 "strings" 14 15 "github.com/sirupsen/logrus" 16 17 "github.com/cilium/cilium/api/v1/models" 18 "github.com/cilium/cilium/pkg/byteorder" 19 "github.com/cilium/cilium/pkg/cidr" 20 "github.com/cilium/cilium/pkg/common" 21 "github.com/cilium/cilium/pkg/defaults" 22 "github.com/cilium/cilium/pkg/lock" 23 "github.com/cilium/cilium/pkg/logging/logfields" 24 "github.com/cilium/cilium/pkg/option" 25 wgTypes "github.com/cilium/cilium/pkg/wireguard/types" 26 ) 27 28 const preferPublicIP bool = true 29 30 var ( 31 // addrsMu protects addrs. Outside the addresses struct 32 // so that we can Uninitialize() without linter complaining 33 // about lock copying. 34 addrsMu lock.RWMutex 35 addrs addresses 36 37 // localNode holds the current state of the local "types.Node". 38 // This is defined here until all uses of the getters and 39 // setters in this file have been migrated to use LocalNodeStore 40 // directly. 41 // Initialized to proper instance via an invoke function in LocalNodeStoreCell, 42 // or temporarily in tests with 'WithTestLocalNodeStore'. 43 localNode *LocalNodeStore 44 ) 45 46 func getLocalNode() LocalNode { 47 n, err := localNode.Get(context.TODO()) 48 if err != nil { 49 // Only expecting errors if we're called after LocalNodeStore has stopped, e.g. 50 // we have a component that uses the legacy getters and setters here and does 51 // not depend on LocalNodeStore. 52 log.WithError(err).Fatal("getLocalNode: unexpected error") 53 } 54 return n 55 } 56 57 type addresses struct { 58 ipv4Loopback net.IP 59 routerInfo RouterInfo 60 } 61 62 type RouterInfo interface { 63 GetIPv4CIDRs() []net.IPNet 64 } 65 66 func makeIPv6HostIP() net.IP { 67 ipstr := "fc00::10CA:1" 68 ip := net.ParseIP(ipstr) 69 if ip == nil { 70 log.WithField(logfields.IPAddr, ipstr).Fatal("Unable to parse IP") 71 } 72 73 return ip 74 } 75 76 // InitDefaultPrefix initializes the node address and allocation prefixes with 77 // default values derived from the system. device can be set to the primary 78 // network device of the system in which case the first address with global 79 // scope will be regarded as the system's node address. 80 func InitDefaultPrefix(device string) { 81 localNode.Update(func(n *LocalNode) { 82 SetDefaultPrefix(option.Config, device, n) 83 }) 84 } 85 86 func SetDefaultPrefix(cfg *option.DaemonConfig, device string, node *LocalNode) { 87 if cfg.EnableIPv4 { 88 isIPv6 := false 89 90 ip, err := firstGlobalV4Addr(device, node.GetCiliumInternalIP(isIPv6), preferPublicIP) 91 if err != nil { 92 return 93 } 94 95 if node.GetNodeIP(isIPv6) == nil { 96 node.SetNodeInternalIP(ip) 97 } 98 99 ipv4range := node.IPv4AllocCIDR 100 ipv6range := node.IPv6AllocCIDR 101 102 if ipv4range == nil { 103 // If the IPv6AllocRange is not nil then the IPv4 allocation should be 104 // derived from the IPv6AllocRange. 105 // vvvv vvvv 106 // FD00:0000:0000:0000:0000:0000:0000:0000 107 if ipv6range != nil { 108 ip = net.IPv4( 109 ipv6range.IP[8], 110 ipv6range.IP[9], 111 ipv6range.IP[10], 112 ipv6range.IP[11]) 113 } 114 v4range := fmt.Sprintf(defaults.DefaultIPv4Prefix+"/%d", 115 ip.To4()[3], defaults.DefaultIPv4PrefixLen) 116 _, ip4net, err := net.ParseCIDR(v4range) 117 if err != nil { 118 log.WithError(err).WithField(logfields.V4Prefix, v4range).Panic("BUG: Invalid default IPv4 prefix") 119 } 120 121 node.IPv4AllocCIDR = cidr.NewCIDR(ip4net) 122 log.WithField(logfields.V4Prefix, node.IPv4AllocCIDR).Info("Using autogenerated IPv4 allocation range") 123 } 124 } 125 126 if cfg.EnableIPv6 { 127 isIPv6 := true 128 ipv4range := node.IPv4AllocCIDR 129 ipv6range := node.IPv6AllocCIDR 130 131 if node.GetNodeIP(isIPv6) == nil { 132 // Find a IPv6 node address first 133 addr, _ := firstGlobalV6Addr(device, node.GetCiliumInternalIP(isIPv6), preferPublicIP) 134 if addr == nil { 135 addr = makeIPv6HostIP() 136 } 137 node.SetNodeInternalIP(addr) 138 } 139 140 if ipv6range == nil && ipv4range != nil { 141 // The IPv6 allocation should be derived from the IPv4 allocation. 142 ip := ipv4range.IP 143 v6range := fmt.Sprintf("%s%02x%02x:%02x%02x:0:0/%d", 144 cfg.IPv6ClusterAllocCIDRBase, ip[0], ip[1], ip[2], ip[3], 96) 145 146 _, ip6net, err := net.ParseCIDR(v6range) 147 if err != nil { 148 log.WithError(err).WithField(logfields.V6Prefix, v6range).Panic("BUG: Invalid default IPv6 prefix") 149 } 150 151 node.IPv6AllocCIDR = cidr.NewCIDR(ip6net) 152 log.WithField(logfields.V6Prefix, node.IPv6AllocCIDR).Info("Using autogenerated IPv6 allocation range") 153 } 154 } 155 } 156 157 func clone(ip net.IP) net.IP { 158 if ip == nil { 159 return nil 160 } 161 dup := make(net.IP, len(ip)) 162 copy(dup, ip) 163 return dup 164 } 165 166 // GetIPv4Loopback returns the loopback IPv4 address of this node. 167 func GetIPv4Loopback() net.IP { 168 addrsMu.RLock() 169 defer addrsMu.RUnlock() 170 return clone(addrs.ipv4Loopback) 171 } 172 173 // SetIPv4Loopback sets the loopback IPv4 address of this node. 174 func SetIPv4Loopback(ip net.IP) { 175 addrsMu.Lock() 176 addrs.ipv4Loopback = clone(ip) 177 addrsMu.Unlock() 178 } 179 180 // GetIPv4AllocRange returns the IPv4 allocation prefix of this node 181 func GetIPv4AllocRange() *cidr.CIDR { 182 return getLocalNode().IPv4AllocCIDR.DeepCopy() 183 } 184 185 // GetIPv6AllocRange returns the IPv6 allocation prefix of this node 186 func GetIPv6AllocRange() *cidr.CIDR { 187 return getLocalNode().IPv6AllocCIDR.DeepCopy() 188 } 189 190 // GetIPv4 returns one of the IPv4 node address available with the following 191 // priority: 192 // - NodeInternalIP 193 // - NodeExternalIP 194 // - other IP address type. 195 // It must be reachable on the network. 196 func GetIPv4() net.IP { 197 n := getLocalNode() 198 return clone(n.GetNodeIP(false)) 199 } 200 201 // GetInternalIPv4 returns node internal ipv4 address else return nil. 202 func GetInternalIPv4() net.IP { 203 n := getLocalNode() 204 return clone(n.GetNodeInternalIPv4()) 205 } 206 207 // GetInternalIPv6 returns node internal ipv6 address else return nil. 208 func GetInternalIPv6() net.IP { 209 n := getLocalNode() 210 return clone(n.GetNodeInternalIPv6()) 211 } 212 213 // GetCiliumEndpointNodeIP is the node IP that will be referenced by CiliumEndpoints with endpoints 214 // running on this node. 215 func GetCiliumEndpointNodeIP() string { 216 if option.Config.EnableIPv4 { 217 return GetIPv4().String() 218 } 219 return GetIPv6().String() 220 } 221 222 // SetInternalIPv4Router sets the cilium internal IPv4 node address, it is allocated from the node prefix. 223 // This must not be conflated with k8s internal IP as this IP address is only relevant within the 224 // Cilium-managed network (this means within the node for direct routing mode and on the overlay 225 // for tunnel mode). 226 func SetInternalIPv4Router(ip net.IP) { 227 localNode.Update(func(n *LocalNode) { 228 n.SetCiliumInternalIP(ip) 229 }) 230 } 231 232 // GetInternalIPv4Router returns the cilium internal IPv4 node address. This must not be conflated with 233 // k8s internal IP as this IP address is only relevant within the Cilium-managed network (this means 234 // within the node for direct routing mode and on the overlay for tunnel mode). 235 func GetInternalIPv4Router() net.IP { 236 n := getLocalNode() 237 return n.GetCiliumInternalIP(false) 238 } 239 240 // GetK8sExternalIPv4 returns the external IPv4 node address. It must be a public IP that is routable 241 // on the network as well as the internet. It can return nil if no External IPv4 address is assigned. 242 func GetK8sExternalIPv4() net.IP { 243 n := getLocalNode() 244 return n.GetExternalIP(false) 245 } 246 247 // GetRouterInfo returns additional information for the router, the cilium_host interface. 248 func GetRouterInfo() RouterInfo { 249 addrsMu.RLock() 250 defer addrsMu.RUnlock() 251 return addrs.routerInfo 252 } 253 254 // SetRouterInfo sets additional information for the router, the cilium_host interface. 255 func SetRouterInfo(info RouterInfo) { 256 addrsMu.Lock() 257 addrs.routerInfo = info 258 addrsMu.Unlock() 259 } 260 261 // GetHostMasqueradeIPv4 returns the IPv4 address to be used for masquerading 262 // any traffic that is being forwarded from the host into the Cilium cluster. 263 func GetHostMasqueradeIPv4() net.IP { 264 return GetInternalIPv4Router() 265 } 266 267 // SetIPv4AllocRange sets the IPv4 address pool to use when allocating 268 // addresses for local endpoints 269 func SetIPv4AllocRange(net *cidr.CIDR) { 270 localNode.Update(func(n *LocalNode) { 271 n.IPv4AllocCIDR = net 272 }) 273 } 274 275 // SetIPv6NodeRange sets the IPv6 address pool to be used on this node 276 func SetIPv6NodeRange(net *cidr.CIDR) { 277 localNode.Update(func(n *LocalNode) { 278 n.IPv6AllocCIDR = net 279 }) 280 } 281 282 // AutoComplete completes the parts of addressing that can be auto derived 283 func AutoComplete() error { 284 InitDefaultPrefix(option.Config.DirectRoutingDevice) 285 286 if option.Config.EnableIPv6 && GetIPv6AllocRange() == nil { 287 return fmt.Errorf("IPv6 allocation CIDR is not configured. Please specify --%s", option.IPv6Range) 288 } 289 290 if option.Config.EnableIPv4 && GetIPv4AllocRange() == nil { 291 return fmt.Errorf("IPv4 allocation CIDR is not configured. Please specify --%s", option.IPv4Range) 292 } 293 294 return nil 295 } 296 297 // ValidatePostInit validates the entire addressing setup and completes it as 298 // required 299 func ValidatePostInit() error { 300 if option.Config.EnableIPv4 || option.Config.TunnelingEnabled() { 301 if GetIPv4() == nil { 302 return fmt.Errorf("external IPv4 node address could not be derived, please configure via --ipv4-node") 303 } 304 } 305 306 if option.Config.EnableIPv4 && GetInternalIPv4Router() == nil { 307 return fmt.Errorf("BUG: Internal IPv4 node address was not configured") 308 } 309 310 return nil 311 } 312 313 // GetIPv6 returns the IPv6 address of the node 314 func GetIPv6() net.IP { 315 n := getLocalNode() 316 return clone(n.GetNodeIP(true)) 317 } 318 319 // GetHostMasqueradeIPv6 returns the IPv6 address to be used for masquerading 320 // any traffic that is being forwarded from the host into the Cilium cluster. 321 func GetHostMasqueradeIPv6() net.IP { 322 return GetIPv6Router() 323 } 324 325 // GetIPv6Router returns the IPv6 address of the router, e.g. address 326 // of cilium_host device. 327 func GetIPv6Router() net.IP { 328 n := getLocalNode() 329 return clone(n.GetCiliumInternalIP(true)) 330 } 331 332 // SetIPv6Router sets the IPv6 address of the router address, e.g. address 333 // of cilium_host device. 334 func SetIPv6Router(ip net.IP) { 335 localNode.Update(func(n *LocalNode) { 336 n.SetCiliumInternalIP(ip) 337 }) 338 } 339 340 // GetK8sExternalIPv6 returns the external IPv6 node address. 341 func GetK8sExternalIPv6() net.IP { 342 n := getLocalNode() 343 return clone(n.GetExternalIP(false)) 344 } 345 346 // GetNodeAddressing returns the NodeAddressing model for the local IPs. 347 func GetNodeAddressing() *models.NodeAddressing { 348 a := &models.NodeAddressing{} 349 350 if option.Config.EnableIPv6 { 351 a.IPV6 = &models.NodeAddressingElement{ 352 Enabled: option.Config.EnableIPv6, 353 IP: GetIPv6Router().String(), 354 AllocRange: GetIPv6AllocRange().String(), 355 } 356 } 357 358 if option.Config.EnableIPv4 { 359 a.IPV4 = &models.NodeAddressingElement{ 360 Enabled: option.Config.EnableIPv4, 361 IP: GetInternalIPv4Router().String(), 362 AllocRange: GetIPv4AllocRange().String(), 363 } 364 } 365 366 return a 367 } 368 369 func getCiliumHostIPsFromFile(nodeConfig string) (ipv4GW, ipv6Router net.IP) { 370 // ipLen is the length of the IP address stored in the node_config.h 371 // it has the same length for both IPv4 and IPv6. 372 const ipLen = net.IPv6len 373 374 var hasIPv4, hasIPv6 bool 375 f, err := os.Open(nodeConfig) 376 switch { 377 case err != nil: 378 default: 379 defer f.Close() 380 scanner := bufio.NewScanner(f) 381 for scanner.Scan() { 382 txt := scanner.Text() 383 switch { 384 case !hasIPv6 && strings.Contains(txt, defaults.RestoreV6Addr): 385 defineLine := strings.Split(txt, defaults.RestoreV6Addr) 386 if len(defineLine) != 2 { 387 continue 388 } 389 ipv6 := common.C2GoArray(defineLine[1]) 390 if len(ipv6) != ipLen { 391 continue 392 } 393 ipv6Router = net.IP(ipv6) 394 hasIPv6 = true 395 case !hasIPv4 && strings.Contains(txt, defaults.RestoreV4Addr): 396 defineLine := strings.Split(txt, defaults.RestoreV4Addr) 397 if len(defineLine) != 2 { 398 continue 399 } 400 ipv4 := common.C2GoArray(defineLine[1]) 401 if len(ipv4) != ipLen { 402 continue 403 } 404 ipv4GW = net.IP(ipv4) 405 hasIPv4 = true 406 407 // Legacy cases based on the header defines: 408 case !hasIPv4 && strings.Contains(txt, "IPV4_GATEWAY"): 409 // #define IPV4_GATEWAY 0xee1c000a 410 defineLine := strings.Split(txt, " ") 411 if len(defineLine) != 3 { 412 continue 413 } 414 ipv4GWHex := strings.TrimPrefix(defineLine[2], "0x") 415 ipv4GWUint64, err := strconv.ParseUint(ipv4GWHex, 16, 32) 416 if err != nil { 417 continue 418 } 419 if ipv4GWUint64 != 0 { 420 bs := make([]byte, net.IPv4len) 421 byteorder.Native.PutUint32(bs, uint32(ipv4GWUint64)) 422 ipv4GW = net.IPv4(bs[0], bs[1], bs[2], bs[3]) 423 hasIPv4 = true 424 } 425 case !hasIPv6 && strings.Contains(txt, " ROUTER_IP "): 426 // #define ROUTER_IP 0xf0, 0xd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8a, 0xd6 427 defineLine := strings.Split(txt, " ROUTER_IP ") 428 if len(defineLine) != 2 { 429 continue 430 } 431 ipv6 := common.C2GoArray(defineLine[1]) 432 if len(ipv6) != net.IPv6len { 433 continue 434 } 435 ipv6Router = net.IP(ipv6) 436 hasIPv6 = true 437 } 438 } 439 } 440 return ipv4GW, ipv6Router 441 } 442 443 // ExtractCiliumHostIPFromFS returns the Cilium IPv4 gateway and router IPv6 address from 444 // the node_config.h file if is present; or by deriving it from 445 // defaults.HostDevice interface, on which only the IPv4 is possible to derive. 446 func ExtractCiliumHostIPFromFS() (ipv4GW, ipv6Router net.IP) { 447 nodeConfig := option.Config.GetNodeConfigPath() 448 ipv4GW, ipv6Router = getCiliumHostIPsFromFile(nodeConfig) 449 if ipv4GW != nil || ipv6Router != nil { 450 log.WithFields(logrus.Fields{ 451 "ipv4": ipv4GW, 452 "ipv6": ipv6Router, 453 "file": nodeConfig, 454 }).Info("Restored router address from node_config") 455 return ipv4GW, ipv6Router 456 } 457 return getCiliumHostIPsFromNetDev(defaults.HostDevice) 458 } 459 460 // SetIPsecKeyIdentity sets the IPsec key identity an opaque value used to 461 // identity encryption keys used on the node. 462 func SetIPsecKeyIdentity(id uint8) { 463 localNode.Update(func(n *LocalNode) { 464 n.EncryptionKey = id 465 }) 466 } 467 468 // GetK8sNodeIPs returns k8s Node IP addr. 469 func GetK8sNodeIP() net.IP { 470 n := getLocalNode() 471 return n.GetK8sNodeIP() 472 } 473 474 func GetWireguardPubKey() string { 475 return getLocalNode().WireguardPubKey 476 } 477 478 func GetOptOutNodeEncryption() bool { 479 return getLocalNode().OptOutNodeEncryption 480 } 481 482 // SetEndpointHealthIPv4 sets the IPv4 cilium-health endpoint address. 483 func SetEndpointHealthIPv4(ip net.IP) { 484 localNode.Update(func(n *LocalNode) { 485 n.IPv4HealthIP = ip 486 }) 487 } 488 489 // GetEndpointHealthIPv4 returns the IPv4 cilium-health endpoint address. 490 func GetEndpointHealthIPv4() net.IP { 491 return getLocalNode().IPv4HealthIP 492 } 493 494 // SetEndpointHealthIPv6 sets the IPv6 cilium-health endpoint address. 495 func SetEndpointHealthIPv6(ip net.IP) { 496 localNode.Update(func(n *LocalNode) { 497 n.IPv6HealthIP = ip 498 }) 499 } 500 501 // GetEndpointHealthIPv6 returns the IPv6 cilium-health endpoint address. 502 func GetEndpointHealthIPv6() net.IP { 503 return getLocalNode().IPv6HealthIP 504 } 505 506 // SetIngressIPv4 sets the local IPv4 source address for Cilium Ingress. 507 func SetIngressIPv4(ip net.IP) { 508 localNode.Update(func(n *LocalNode) { 509 n.IPv4IngressIP = ip 510 }) 511 } 512 513 // GetIngressIPv4 returns the local IPv4 source address for Cilium Ingress. 514 func GetIngressIPv4() net.IP { 515 return getLocalNode().IPv4IngressIP 516 } 517 518 // SetIngressIPv6 sets the local IPv6 source address for Cilium Ingress. 519 func SetIngressIPv6(ip net.IP) { 520 localNode.Update(func(n *LocalNode) { 521 n.IPv6IngressIP = ip 522 }) 523 } 524 525 // GetIngressIPv6 returns the local IPv6 source address for Cilium Ingress. 526 func GetIngressIPv6() net.IP { 527 return getLocalNode().IPv6IngressIP 528 } 529 530 // GetEndpointEncryptKeyIndex returns the encryption key value for an endpoint 531 // owned by the local node. 532 // With IPSec encryption, this is the ID of the currently loaded key. 533 // With WireGuard, this returns a non-zero static value. 534 // Note that the key index returned by this function is only valid for _endpoints_ 535 // of the local node. If you want to obtain the key index of the local node itself, 536 // access the `EncryptionKey` field via the LocalNodeStore. 537 func GetEndpointEncryptKeyIndex() uint8 { 538 switch { 539 case option.Config.EnableIPSec: 540 return getLocalNode().EncryptionKey 541 case option.Config.EnableWireguard: 542 return wgTypes.StaticEncryptKey 543 544 } 545 return 0 546 } 547 548 // WithTestLocalNodeStore sets the 'localNode' to a temporary instance and 549 // runs the given test. Afterwards the 'localNode' is restored to nil. 550 // This is a temporary workaround for tests until the LocalNodeStoreCell can be 551 // used. 552 func WithTestLocalNodeStore(runTest func()) { 553 SetTestLocalNodeStore() 554 defer UnsetTestLocalNodeStore() 555 runTest() 556 } 557 558 func SetTestLocalNodeStore() { 559 if localNode != nil { 560 panic("localNode already set") 561 } 562 563 // Set the localNode global variable temporarily so that the legacy getters 564 // and setters can access it. 565 localNode = NewTestLocalNodeStore(LocalNode{}) 566 } 567 568 func UnsetTestLocalNodeStore() { 569 localNode = nil 570 } 571 572 // UpdateLocalNodeInTest provides access to modifying the local node 573 // information from tests that are not yet using hive and the LocalNodeStoreCell. 574 func UpdateLocalNodeInTest(mod func(n *LocalNode)) { 575 if localNode == nil { 576 panic("localNode not set, use node.LocalNodeStoreCell or WithTestLocalNodeStore()?") 577 } 578 localNode.Update(mod) 579 }