github.com/cilium/cilium@v1.16.2/pkg/node/types/node.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package types 5 6 import ( 7 "encoding/json" 8 "errors" 9 "fmt" 10 "net" 11 "path" 12 "slices" 13 14 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 16 "github.com/cilium/cilium/api/v1/models" 17 "github.com/cilium/cilium/pkg/annotation" 18 "github.com/cilium/cilium/pkg/cidr" 19 cmtypes "github.com/cilium/cilium/pkg/clustermesh/types" 20 "github.com/cilium/cilium/pkg/defaults" 21 ipamTypes "github.com/cilium/cilium/pkg/ipam/types" 22 ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 23 "github.com/cilium/cilium/pkg/kvstore/store" 24 "github.com/cilium/cilium/pkg/node/addressing" 25 "github.com/cilium/cilium/pkg/option" 26 "github.com/cilium/cilium/pkg/source" 27 ) 28 29 // Identity represents the node identity of a node. 30 type Identity struct { 31 Name string 32 Cluster string 33 } 34 35 // String returns the string representation on NodeIdentity. 36 func (nn Identity) String() string { 37 return path.Join(nn.Cluster, nn.Name) 38 } 39 40 // appendAllocCDIR sets or appends the given podCIDR to the node. 41 // If the IPv4/IPv6AllocCIDR is already set, we add the podCIDR as a secondary 42 // alloc CIDR. 43 func (n *Node) appendAllocCDIR(podCIDR *cidr.CIDR) { 44 if podCIDR.IP.To4() != nil { 45 if n.IPv4AllocCIDR == nil { 46 n.IPv4AllocCIDR = podCIDR 47 } else { 48 n.IPv4SecondaryAllocCIDRs = append(n.IPv4SecondaryAllocCIDRs, podCIDR) 49 } 50 } else { 51 if n.IPv6AllocCIDR == nil { 52 n.IPv6AllocCIDR = podCIDR 53 } else { 54 n.IPv6SecondaryAllocCIDRs = append(n.IPv6SecondaryAllocCIDRs, podCIDR) 55 } 56 } 57 } 58 59 // ParseCiliumNode parses a CiliumNode custom resource and returns a Node 60 // instance. Invalid IP and CIDRs are silently ignored 61 func ParseCiliumNode(n *ciliumv2.CiliumNode) (node Node) { 62 wireguardPubKey, _ := annotation.Get(n, annotation.WireguardPubKey, annotation.WireguardPubKeyAlias) 63 node = Node{ 64 Name: n.Name, 65 EncryptionKey: uint8(n.Spec.Encryption.Key), 66 Cluster: option.Config.ClusterName, 67 ClusterID: option.Config.ClusterID, 68 Source: source.CustomResource, 69 Labels: n.ObjectMeta.Labels, 70 Annotations: n.ObjectMeta.Annotations, 71 NodeIdentity: uint32(n.Spec.NodeIdentity), 72 WireguardPubKey: wireguardPubKey, 73 BootID: n.Spec.BootID, 74 } 75 76 for _, cidrString := range n.Spec.IPAM.PodCIDRs { 77 ipnet, err := cidr.ParseCIDR(cidrString) 78 if err == nil { 79 node.appendAllocCDIR(ipnet) 80 } 81 } 82 83 for _, pool := range n.Spec.IPAM.Pools.Allocated { 84 for _, podCIDR := range pool.CIDRs { 85 ipnet, err := cidr.ParseCIDR(string(podCIDR)) 86 if err == nil { 87 node.appendAllocCDIR(ipnet) 88 } 89 } 90 } 91 92 node.IPv4HealthIP = net.ParseIP(n.Spec.HealthAddressing.IPv4) 93 node.IPv6HealthIP = net.ParseIP(n.Spec.HealthAddressing.IPv6) 94 95 node.IPv4IngressIP = net.ParseIP(n.Spec.IngressAddressing.IPV4) 96 node.IPv6IngressIP = net.ParseIP(n.Spec.IngressAddressing.IPV6) 97 98 for _, address := range n.Spec.Addresses { 99 if ip := net.ParseIP(address.IP); ip != nil { 100 node.IPAddresses = append(node.IPAddresses, Address{Type: address.Type, IP: ip}) 101 } 102 } 103 104 return 105 } 106 107 // ToCiliumNode converts the node to a CiliumNode 108 func (n *Node) ToCiliumNode() *ciliumv2.CiliumNode { 109 var ( 110 podCIDRs []string 111 ipAddrs []ciliumv2.NodeAddress 112 healthIPv4, healthIPv6 string 113 ingressIPv4, ingressIPv6 string 114 ) 115 116 if n.IPv4AllocCIDR != nil { 117 podCIDRs = append(podCIDRs, n.IPv4AllocCIDR.String()) 118 } 119 if n.IPv6AllocCIDR != nil { 120 podCIDRs = append(podCIDRs, n.IPv6AllocCIDR.String()) 121 } 122 for _, ipv4AllocCIDR := range n.IPv4SecondaryAllocCIDRs { 123 podCIDRs = append(podCIDRs, ipv4AllocCIDR.String()) 124 } 125 for _, ipv6AllocCIDR := range n.IPv6SecondaryAllocCIDRs { 126 podCIDRs = append(podCIDRs, ipv6AllocCIDR.String()) 127 } 128 if n.IPv4HealthIP != nil { 129 healthIPv4 = n.IPv4HealthIP.String() 130 } 131 if n.IPv6HealthIP != nil { 132 healthIPv6 = n.IPv6HealthIP.String() 133 } 134 if n.IPv4IngressIP != nil { 135 ingressIPv4 = n.IPv4IngressIP.String() 136 } 137 if n.IPv6IngressIP != nil { 138 ingressIPv6 = n.IPv6IngressIP.String() 139 } 140 141 for _, address := range n.IPAddresses { 142 ipAddrs = append(ipAddrs, ciliumv2.NodeAddress{ 143 Type: address.Type, 144 IP: address.IP.String(), 145 }) 146 } 147 148 return &ciliumv2.CiliumNode{ 149 ObjectMeta: v1.ObjectMeta{ 150 Name: n.Name, 151 Labels: n.Labels, 152 Annotations: n.Annotations, 153 }, 154 Spec: ciliumv2.NodeSpec{ 155 Addresses: ipAddrs, 156 HealthAddressing: ciliumv2.HealthAddressingSpec{ 157 IPv4: healthIPv4, 158 IPv6: healthIPv6, 159 }, 160 IngressAddressing: ciliumv2.AddressPair{ 161 IPV4: ingressIPv4, 162 IPV6: ingressIPv6, 163 }, 164 Encryption: ciliumv2.EncryptionSpec{ 165 Key: int(n.EncryptionKey), 166 }, 167 IPAM: ipamTypes.IPAMSpec{ 168 PodCIDRs: podCIDRs, 169 }, 170 NodeIdentity: uint64(n.NodeIdentity), 171 BootID: n.BootID, 172 }, 173 } 174 } 175 176 // RegisterNode overloads GetKeyName to ignore the cluster name, as cluster name may not be stable during node registration. 177 // 178 // +k8s:deepcopy-gen=true 179 type RegisterNode struct { 180 Node 181 } 182 183 // GetKeyName Overloaded key name w/o cluster name 184 func (n *RegisterNode) GetKeyName() string { 185 return n.Name 186 } 187 188 // DeepKeyCopy creates a deep copy of the LocalKey 189 func (n *RegisterNode) DeepKeyCopy() store.LocalKey { 190 return n.DeepCopy() 191 } 192 193 func (n *RegisterNode) Unmarshal(_ string, data []byte) error { 194 newNode := Node{} 195 if err := json.Unmarshal(data, &newNode); err != nil { 196 return err 197 } 198 199 n.Node = newNode 200 return nil 201 } 202 203 // Node contains the nodes name, the list of addresses to this address 204 // 205 // +k8s:deepcopy-gen=true 206 type Node struct { 207 // Name is the name of the node. This is typically the hostname of the node. 208 Name string 209 210 // Cluster is the name of the cluster the node is associated with 211 Cluster string 212 213 IPAddresses []Address 214 215 // IPv4AllocCIDR if set, is the IPv4 address pool out of which the node 216 // allocates IPs for local endpoints from 217 IPv4AllocCIDR *cidr.CIDR 218 219 // IPv4SecondaryAllocCIDRs contains additional IPv4 CIDRs from which this 220 //node allocates IPs for its local endpoints from 221 IPv4SecondaryAllocCIDRs []*cidr.CIDR 222 223 // IPv6AllocCIDR if set, is the IPv6 address pool out of which the node 224 // allocates IPs for local endpoints from 225 IPv6AllocCIDR *cidr.CIDR 226 227 // IPv6SecondaryAllocCIDRs contains additional IPv6 CIDRs from which this 228 // node allocates IPs for its local endpoints from 229 IPv6SecondaryAllocCIDRs []*cidr.CIDR 230 231 // IPv4HealthIP if not nil, this is the IPv4 address of the 232 // cilium-health endpoint located on the node. 233 IPv4HealthIP net.IP 234 235 // IPv6HealthIP if not nil, this is the IPv6 address of the 236 // cilium-health endpoint located on the node. 237 IPv6HealthIP net.IP 238 239 // IPv4IngressIP if not nil, this is the IPv4 address of the 240 // Ingress listener on the node. 241 IPv4IngressIP net.IP 242 243 // IPv6IngressIP if not nil, this is the IPv6 address of the 244 // Ingress listener located on the node. 245 IPv6IngressIP net.IP 246 247 // ClusterID is the unique identifier of the cluster 248 ClusterID uint32 249 250 // Source is the source where the node configuration was generated / created. 251 Source source.Source 252 253 // Key index used for transparent encryption or 0 for no encryption 254 EncryptionKey uint8 255 256 // Node labels 257 Labels map[string]string 258 259 // Node annotations 260 Annotations map[string]string 261 262 // NodeIdentity is the numeric identity allocated for the node 263 NodeIdentity uint32 264 265 // WireguardPubKey is the WireGuard public key of this node 266 WireguardPubKey string 267 268 // BootID is a unique node identifier generated on boot 269 BootID string 270 } 271 272 // Fullname returns the node's full name including the cluster name if a 273 // cluster name value other than the default value has been specified 274 func (n *Node) Fullname() string { 275 if n.Cluster != defaults.ClusterName { 276 return path.Join(n.Cluster, n.Name) 277 } 278 279 return n.Name 280 } 281 282 // Address is a node address which contains an IP and the address type. 283 // 284 // +k8s:deepcopy-gen=true 285 type Address struct { 286 Type addressing.AddressType 287 IP net.IP 288 } 289 290 func (a Address) ToString() string { 291 return a.IP.String() 292 } 293 294 func (a Address) AddrType() addressing.AddressType { 295 return a.Type 296 } 297 298 // GetNodeIP returns one of the node's IP addresses available with the 299 // following priority: 300 // - NodeInternalIP 301 // - NodeExternalIP 302 // - other IP address type 303 // Nil is returned if GetNodeIP fails to extract an IP from the Node based 304 // on the provided address family. 305 func (n *Node) GetNodeIP(ipv6 bool) net.IP { 306 return addressing.ExtractNodeIP[Address](n.IPAddresses, ipv6) 307 } 308 309 // GetExternalIP returns ExternalIP of k8s Node. If not present, then it 310 // returns nil; 311 func (n *Node) GetExternalIP(ipv6 bool) net.IP { 312 for _, addr := range n.IPAddresses { 313 if (ipv6 && addr.IP.To4() != nil) || (!ipv6 && addr.IP.To4() == nil) { 314 continue 315 } 316 if addr.Type == addressing.NodeExternalIP { 317 return addr.IP 318 } 319 } 320 321 return nil 322 } 323 324 // GetK8sNodeIPs returns k8s Node IP (either InternalIP or ExternalIP or nil; 325 // the former is preferred). 326 func (n *Node) GetK8sNodeIP() net.IP { 327 var externalIP net.IP 328 329 for _, addr := range n.IPAddresses { 330 if addr.Type == addressing.NodeInternalIP { 331 return addr.IP 332 } else if addr.Type == addressing.NodeExternalIP { 333 externalIP = addr.IP 334 } 335 } 336 337 return externalIP 338 } 339 340 // GetNodeInternalIP returns the Internal IPv4 of node or nil. 341 func (n *Node) GetNodeInternalIPv4() net.IP { 342 for _, addr := range n.IPAddresses { 343 if addr.IP.To4() == nil { 344 continue 345 } 346 if addr.Type == addressing.NodeInternalIP { 347 return addr.IP 348 } 349 } 350 351 return nil 352 } 353 354 // GetNodeInternalIP returns the Internal IPv6 of node or nil. 355 func (n *Node) GetNodeInternalIPv6() net.IP { 356 for _, addr := range n.IPAddresses { 357 if addr.IP.To4() != nil { 358 continue 359 } 360 if addr.Type == addressing.NodeInternalIP { 361 return addr.IP 362 } 363 } 364 365 return nil 366 } 367 368 // GetCiliumInternalIP returns the CiliumInternalIP e.g. the IP associated 369 // with cilium_host on the node. 370 func (n *Node) GetCiliumInternalIP(ipv6 bool) net.IP { 371 for _, addr := range n.IPAddresses { 372 if (ipv6 && addr.IP.To4() != nil) || 373 (!ipv6 && addr.IP.To4() == nil) { 374 continue 375 } 376 if addr.Type == addressing.NodeCiliumInternalIP { 377 return addr.IP 378 } 379 } 380 return nil 381 } 382 383 // SetCiliumInternalIP sets the CiliumInternalIP e.g. the IP associated 384 // with cilium_host on the node. 385 func (n *Node) SetCiliumInternalIP(newAddr net.IP) { 386 n.setAddress(addressing.NodeCiliumInternalIP, newAddr) 387 } 388 389 // SetNodeExternalIP sets the NodeExternalIP. 390 func (n *Node) SetNodeExternalIP(newAddr net.IP) { 391 n.setAddress(addressing.NodeExternalIP, newAddr) 392 } 393 394 // SetNodeInternalIP sets the NodeInternalIP. 395 func (n *Node) SetNodeInternalIP(newAddr net.IP) { 396 n.setAddress(addressing.NodeInternalIP, newAddr) 397 } 398 399 func (n *Node) RemoveAddresses(typ addressing.AddressType) { 400 newAddresses := []Address{} 401 for _, addr := range n.IPAddresses { 402 if addr.Type != typ { 403 newAddresses = append(newAddresses, addr) 404 } 405 } 406 n.IPAddresses = newAddresses 407 } 408 409 func (n *Node) setAddress(typ addressing.AddressType, newIP net.IP) { 410 newAddr := Address{Type: typ, IP: newIP} 411 412 if newIP == nil { 413 n.RemoveAddresses(typ) 414 return 415 } 416 417 // Create a copy of the slice, so that we don't modify the 418 // current one, which may be captured by any of the observers. 419 n.IPAddresses = slices.Clone(n.IPAddresses) 420 421 ipv6 := newIP.To4() == nil 422 // Try first to replace an existing address with same type 423 for i, addr := range n.IPAddresses { 424 if addr.Type != typ { 425 continue 426 } 427 if ipv6 != (addr.IP.To4() == nil) { 428 // Don't replace if address family is different. 429 continue 430 } 431 n.IPAddresses[i] = newAddr 432 return 433 } 434 n.IPAddresses = append(n.IPAddresses, newAddr) 435 436 } 437 438 func (n *Node) GetIPByType(addrType addressing.AddressType, ipv6 bool) net.IP { 439 for _, addr := range n.IPAddresses { 440 if addr.Type != addrType { 441 continue 442 } 443 if is4 := addr.IP.To4() != nil; (!ipv6 && is4) || (ipv6 && !is4) { 444 return addr.IP 445 } 446 } 447 return nil 448 } 449 450 func (n *Node) getPrimaryAddress() *models.NodeAddressing { 451 v4 := n.GetNodeIP(false) 452 v6 := n.GetNodeIP(true) 453 454 var ipv4AllocStr, ipv6AllocStr string 455 if n.IPv4AllocCIDR != nil { 456 ipv4AllocStr = n.IPv4AllocCIDR.String() 457 } 458 if n.IPv6AllocCIDR != nil { 459 ipv6AllocStr = n.IPv6AllocCIDR.String() 460 } 461 462 var v4Str, v6Str string 463 if v4 != nil { 464 v4Str = v4.String() 465 } 466 if v6 != nil { 467 v6Str = v6.String() 468 } 469 470 return &models.NodeAddressing{ 471 IPV4: &models.NodeAddressingElement{ 472 Enabled: option.Config.EnableIPv4, 473 IP: v4Str, 474 AllocRange: ipv4AllocStr, 475 }, 476 IPV6: &models.NodeAddressingElement{ 477 Enabled: option.Config.EnableIPv6, 478 IP: v6Str, 479 AllocRange: ipv6AllocStr, 480 }, 481 } 482 } 483 484 func (n *Node) isPrimaryAddress(addr Address, ipv4 bool) bool { 485 return addr.IP.String() == n.GetNodeIP(!ipv4).String() 486 } 487 488 func (n *Node) getSecondaryAddresses() []*models.NodeAddressingElement { 489 result := []*models.NodeAddressingElement{} 490 491 for _, addr := range n.IPAddresses { 492 ipv4 := false 493 if addr.IP.To4() != nil { 494 ipv4 = true 495 } 496 if !n.isPrimaryAddress(addr, ipv4) { 497 result = append(result, &models.NodeAddressingElement{ 498 IP: addr.IP.String(), 499 }) 500 } 501 } 502 503 return result 504 } 505 506 func (n *Node) getHealthAddresses() *models.NodeAddressing { 507 if n.IPv4HealthIP == nil && n.IPv6HealthIP == nil { 508 return nil 509 } 510 511 var v4Str, v6Str string 512 if n.IPv4HealthIP != nil { 513 v4Str = n.IPv4HealthIP.String() 514 } 515 if n.IPv6HealthIP != nil { 516 v6Str = n.IPv6HealthIP.String() 517 } 518 519 return &models.NodeAddressing{ 520 IPV4: &models.NodeAddressingElement{ 521 Enabled: option.Config.EnableIPv4, 522 IP: v4Str, 523 }, 524 IPV6: &models.NodeAddressingElement{ 525 Enabled: option.Config.EnableIPv6, 526 IP: v6Str, 527 }, 528 } 529 } 530 531 func (n *Node) getIngressAddresses() *models.NodeAddressing { 532 if n.IPv4IngressIP == nil && n.IPv6IngressIP == nil { 533 return nil 534 } 535 536 var v4Str, v6Str string 537 if n.IPv4IngressIP != nil { 538 v4Str = n.IPv4IngressIP.String() 539 } 540 if n.IPv6IngressIP != nil { 541 v6Str = n.IPv6IngressIP.String() 542 } 543 544 return &models.NodeAddressing{ 545 IPV4: &models.NodeAddressingElement{ 546 Enabled: option.Config.EnableIPv4, 547 IP: v4Str, 548 }, 549 IPV6: &models.NodeAddressingElement{ 550 Enabled: option.Config.EnableIPv6, 551 IP: v6Str, 552 }, 553 } 554 } 555 556 // GetModel returns the API model representation of a node. 557 func (n *Node) GetModel() *models.NodeElement { 558 return &models.NodeElement{ 559 Name: n.Fullname(), 560 PrimaryAddress: n.getPrimaryAddress(), 561 SecondaryAddresses: n.getSecondaryAddresses(), 562 HealthEndpointAddress: n.getHealthAddresses(), 563 IngressAddress: n.getIngressAddresses(), 564 Source: string(n.Source), 565 } 566 } 567 568 // Identity returns the identity of the node 569 func (n *Node) Identity() Identity { 570 return Identity{ 571 Name: n.Name, 572 Cluster: n.Cluster, 573 } 574 } 575 576 func getCluster() string { 577 return option.Config.ClusterName 578 } 579 580 // IsLocal returns true if this is the node on which the agent itself is 581 // running on 582 func (n *Node) IsLocal() bool { 583 return n != nil && n.Name == GetName() && n.Cluster == getCluster() 584 } 585 586 func (n *Node) GetIPv4AllocCIDRs() []*cidr.CIDR { 587 result := make([]*cidr.CIDR, 0, len(n.IPv4SecondaryAllocCIDRs)+1) 588 if n.IPv4AllocCIDR != nil { 589 result = append(result, n.IPv4AllocCIDR) 590 } 591 if len(n.IPv4SecondaryAllocCIDRs) > 0 { 592 result = append(result, n.IPv4SecondaryAllocCIDRs...) 593 } 594 return result 595 } 596 597 func (n *Node) GetIPv6AllocCIDRs() []*cidr.CIDR { 598 result := make([]*cidr.CIDR, 0, len(n.IPv6SecondaryAllocCIDRs)+1) 599 if n.IPv6AllocCIDR != nil { 600 result = append(result, n.IPv6AllocCIDR) 601 } 602 if len(n.IPv6SecondaryAllocCIDRs) > 0 { 603 result = append(result, n.IPv6SecondaryAllocCIDRs...) 604 } 605 return result 606 } 607 608 // GetKeyNodeName constructs the API name for the given cluster and node name. 609 func GetKeyNodeName(cluster, node string) string { 610 // WARNING - STABLE API: Changing the structure of the key may break 611 // backwards compatibility 612 return path.Join(cluster, node) 613 } 614 615 // GetKeyName returns the kvstore key to be used for the node 616 func (n *Node) GetKeyName() string { 617 return GetKeyNodeName(n.Cluster, n.Name) 618 } 619 620 // DeepKeyCopy creates a deep copy of the LocalKey 621 func (n *Node) DeepKeyCopy() store.LocalKey { 622 return n.DeepCopy() 623 } 624 625 // Marshal returns the node object as JSON byte slice 626 func (n *Node) Marshal() ([]byte, error) { 627 return json.Marshal(n) 628 } 629 630 // Unmarshal parses the JSON byte slice and updates the node receiver 631 func (n *Node) Unmarshal(key string, data []byte) error { 632 newNode := Node{} 633 if err := json.Unmarshal(data, &newNode); err != nil { 634 return err 635 } 636 637 if err := newNode.validate(); err != nil { 638 return err 639 } 640 641 *n = newNode 642 643 return nil 644 } 645 646 // LogRepr returns a representation of the node to be used for logging 647 func (n *Node) LogRepr() string { 648 b, err := n.Marshal() 649 if err != nil { 650 return fmt.Sprintf("%#v", n) 651 } 652 return string(b) 653 } 654 655 func (n *Node) validate() error { 656 switch { 657 case n.Cluster == "": 658 return errors.New("cluster is unset") 659 case n.Name == "": 660 return errors.New("name is unset") 661 } 662 663 // Skip the ClusterID check if it matches the local one, as we assume that 664 // it has already been validated, and to allow it to be zero. 665 if n.ClusterID != option.Config.ClusterID { 666 if err := cmtypes.ValidateClusterID(n.ClusterID); err != nil { 667 return err 668 } 669 } 670 671 return nil 672 }