github.com/cilium/cilium@v1.16.2/pkg/loadbalancer/loadbalancer.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package loadbalancer 5 6 import ( 7 "fmt" 8 "net" 9 "sort" 10 "strconv" 11 "strings" 12 13 "github.com/cilium/cilium/api/v1/models" 14 "github.com/cilium/cilium/pkg/cidr" 15 cmtypes "github.com/cilium/cilium/pkg/clustermesh/types" 16 "github.com/cilium/cilium/pkg/option" 17 ) 18 19 // SVCType is a type of a service. 20 type SVCType string 21 22 const ( 23 SVCTypeNone = SVCType("NONE") 24 SVCTypeHostPort = SVCType("HostPort") 25 SVCTypeClusterIP = SVCType("ClusterIP") 26 SVCTypeNodePort = SVCType("NodePort") 27 SVCTypeExternalIPs = SVCType("ExternalIPs") 28 SVCTypeLoadBalancer = SVCType("LoadBalancer") 29 SVCTypeLocalRedirect = SVCType("LocalRedirect") 30 ) 31 32 // SVCTrafficPolicy defines which backends are chosen 33 type SVCTrafficPolicy string 34 35 const ( 36 SVCTrafficPolicyNone = SVCTrafficPolicy("NONE") 37 SVCTrafficPolicyCluster = SVCTrafficPolicy("Cluster") 38 SVCTrafficPolicyLocal = SVCTrafficPolicy("Local") 39 ) 40 41 // SVCNatPolicy defines whether we need NAT46/64 translation for backends 42 type SVCNatPolicy string 43 44 const ( 45 SVCNatPolicyNone = SVCNatPolicy("NONE") 46 SVCNatPolicyNat46 = SVCNatPolicy("Nat46") 47 SVCNatPolicyNat64 = SVCNatPolicy("Nat64") 48 ) 49 50 // ServiceFlags is the datapath representation of the service flags that can be 51 // used (lb{4,6}_service.flags) 52 type ServiceFlags uint16 53 54 const ( 55 serviceFlagNone = 0 56 serviceFlagExternalIPs = 1 << 0 57 serviceFlagNodePort = 1 << 1 58 serviceFlagExtLocalScope = 1 << 2 59 serviceFlagHostPort = 1 << 3 60 serviceFlagSessionAffinity = 1 << 4 61 serviceFlagLoadBalancer = 1 << 5 62 serviceFlagRoutable = 1 << 6 63 serviceFlagSourceRange = 1 << 7 64 serviceFlagLocalRedirect = 1 << 8 65 serviceFlagNat46x64 = 1 << 9 66 serviceFlagL7LoadBalancer = 1 << 10 67 serviceFlagLoopback = 1 << 11 68 serviceFlagIntLocalScope = 1 << 12 69 serviceFlagTwoScopes = 1 << 13 70 ) 71 72 type SvcFlagParam struct { 73 SvcType SVCType 74 SvcNatPolicy SVCNatPolicy 75 SvcExtLocal bool 76 SvcIntLocal bool 77 SessionAffinity bool 78 IsRoutable bool 79 CheckSourceRange bool 80 L7LoadBalancer bool 81 LoopbackHostport bool 82 } 83 84 // NewSvcFlag creates service flag 85 func NewSvcFlag(p *SvcFlagParam) ServiceFlags { 86 var flags ServiceFlags 87 88 switch p.SvcType { 89 case SVCTypeExternalIPs: 90 flags |= serviceFlagExternalIPs 91 case SVCTypeNodePort: 92 flags |= serviceFlagNodePort 93 case SVCTypeLoadBalancer: 94 flags |= serviceFlagLoadBalancer 95 case SVCTypeHostPort: 96 flags |= serviceFlagHostPort 97 if p.LoopbackHostport { 98 flags |= serviceFlagLoopback 99 } 100 case SVCTypeLocalRedirect: 101 flags |= serviceFlagLocalRedirect 102 } 103 104 switch p.SvcNatPolicy { 105 case SVCNatPolicyNat46: 106 fallthrough 107 case SVCNatPolicyNat64: 108 flags |= serviceFlagNat46x64 109 } 110 111 if p.SvcExtLocal { 112 flags |= serviceFlagExtLocalScope 113 } 114 if p.SvcIntLocal { 115 flags |= serviceFlagIntLocalScope 116 } 117 if p.SessionAffinity { 118 flags |= serviceFlagSessionAffinity 119 } 120 if p.IsRoutable { 121 flags |= serviceFlagRoutable 122 } 123 if p.CheckSourceRange { 124 flags |= serviceFlagSourceRange 125 } 126 if p.L7LoadBalancer { 127 flags |= serviceFlagL7LoadBalancer 128 } 129 if p.SvcExtLocal != p.SvcIntLocal && p.SvcType != SVCTypeClusterIP { 130 flags |= serviceFlagTwoScopes 131 } 132 133 return flags 134 } 135 136 // SVCType returns a service type from the flags 137 func (s ServiceFlags) SVCType() SVCType { 138 switch { 139 case s&serviceFlagExternalIPs != 0: 140 return SVCTypeExternalIPs 141 case s&serviceFlagNodePort != 0: 142 return SVCTypeNodePort 143 case s&serviceFlagLoadBalancer != 0: 144 return SVCTypeLoadBalancer 145 case s&serviceFlagHostPort != 0: 146 return SVCTypeHostPort 147 case s&serviceFlagLocalRedirect != 0: 148 return SVCTypeLocalRedirect 149 default: 150 return SVCTypeClusterIP 151 } 152 } 153 154 func (s ServiceFlags) IsL7LB() bool { 155 return s&serviceFlagL7LoadBalancer != 0 156 } 157 158 // SVCExtTrafficPolicy returns a service traffic policy from the flags 159 func (s ServiceFlags) SVCExtTrafficPolicy() SVCTrafficPolicy { 160 switch { 161 case s&serviceFlagExtLocalScope != 0: 162 return SVCTrafficPolicyLocal 163 default: 164 return SVCTrafficPolicyCluster 165 } 166 } 167 168 // SVCIntTrafficPolicy returns a service traffic policy from the flags 169 func (s ServiceFlags) SVCIntTrafficPolicy() SVCTrafficPolicy { 170 switch { 171 case s&serviceFlagIntLocalScope != 0: 172 return SVCTrafficPolicyLocal 173 default: 174 return SVCTrafficPolicyCluster 175 } 176 } 177 178 // SVCNatPolicy returns a service NAT policy from the flags 179 func (s ServiceFlags) SVCNatPolicy(fe L3n4Addr) SVCNatPolicy { 180 if s&serviceFlagNat46x64 == 0 { 181 return SVCNatPolicyNone 182 } 183 184 if fe.IsIPv6() { 185 return SVCNatPolicyNat64 186 } else { 187 return SVCNatPolicyNat46 188 } 189 } 190 191 // String returns the string implementation of ServiceFlags. 192 func (s ServiceFlags) String() string { 193 var str []string 194 195 str = append(str, string(s.SVCType())) 196 if s&serviceFlagExtLocalScope != 0 { 197 str = append(str, string(SVCTrafficPolicyLocal)) 198 } 199 if s&serviceFlagIntLocalScope != 0 { 200 str = append(str, "Internal"+string(SVCTrafficPolicyLocal)) 201 } 202 if s&serviceFlagTwoScopes != 0 { 203 str = append(str, "two-scopes") 204 } 205 if s&serviceFlagSessionAffinity != 0 { 206 str = append(str, "sessionAffinity") 207 } 208 if s&serviceFlagRoutable == 0 { 209 str = append(str, "non-routable") 210 } 211 if s&serviceFlagSourceRange != 0 { 212 str = append(str, "check source-range") 213 } 214 if s&serviceFlagNat46x64 != 0 { 215 str = append(str, "46x64") 216 } 217 if s&serviceFlagL7LoadBalancer != 0 { 218 str = append(str, "l7-load-balancer") 219 } 220 if s&serviceFlagLoopback != 0 { 221 str = append(str, "loopback") 222 } 223 224 return strings.Join(str, ", ") 225 } 226 227 // UInt8 returns the UInt16 representation of the ServiceFlags. 228 func (s ServiceFlags) UInt16() uint16 { 229 return uint16(s) 230 } 231 232 const ( 233 NONE = L4Type("NONE") 234 // TCP type. 235 TCP = L4Type("TCP") 236 // UDP type. 237 UDP = L4Type("UDP") 238 // SCTP type. 239 SCTP = L4Type("SCTP") 240 ) 241 242 const ( 243 // ScopeExternal is the lookup scope for services from outside the node. 244 ScopeExternal uint8 = iota 245 // ScopeInternal is the lookup scope for services from inside the node. 246 ScopeInternal 247 ) 248 249 // BackendState tracks backend's ability to load-balance service traffic. 250 // 251 // Valid transition states for a backend - 252 // BackendStateActive -> BackendStateTerminating, BackendStateQuarantined, BackendStateMaintenance 253 // BackendStateTerminating -> No valid state transition 254 // BackendStateQuarantined -> BackendStateActive, BackendStateTerminating 255 // BackendStateMaintenance -> BackendStateActive 256 // 257 // Sources setting the states - 258 // BackendStateActive - Kubernetes events, service API 259 // BackendStateTerminating - Kubernetes events 260 // BackendStateQuarantined - service API 261 // BackendStateMaintenance - service API 262 const ( 263 // BackendStateActive refers to the backend state when it's available for 264 // load-balancing traffic. It's the default state for a backend. 265 // Backends in this state can be health-checked. 266 BackendStateActive BackendState = iota 267 // BackendStateTerminating refers to the terminating backend state so that 268 // it can be gracefully removed. 269 // Backends in this state won't be health-checked. 270 BackendStateTerminating 271 // BackendStateQuarantined refers to the backend state when it's unreachable, 272 // and will not be selected for load-balancing traffic. 273 // Backends in this state can be health-checked. 274 BackendStateQuarantined 275 // BackendStateMaintenance refers to the backend state where the backend 276 // is put under maintenance, and will neither be selected for load-balancing 277 // traffic nor be health-checked. 278 BackendStateMaintenance 279 // BackendStateInvalid is an invalid state, and is used to report error conditions. 280 // Keep this as the last entry. 281 BackendStateInvalid 282 ) 283 284 // BackendStateFlags is the datapath representation of the backend flags that 285 // are used in (lb{4,6}_backend.flags) to store backend state. 286 type BackendStateFlags = uint8 287 288 const ( 289 BackendStateActiveFlag = iota 290 BackendStateTerminatingFlag 291 BackendStateQuarantinedFlag 292 BackendStateMaintenanceFlag 293 ) 294 295 func NewBackendFlags(state BackendState) BackendStateFlags { 296 var flags BackendStateFlags 297 298 switch state { 299 case BackendStateActive: 300 flags = BackendStateActiveFlag 301 case BackendStateTerminating: 302 flags = BackendStateTerminatingFlag 303 case BackendStateQuarantined: 304 flags = BackendStateQuarantinedFlag 305 case BackendStateMaintenance: 306 flags = BackendStateMaintenanceFlag 307 } 308 309 return flags 310 } 311 312 func GetBackendStateFromFlags(flags uint8) BackendState { 313 switch flags { 314 case BackendStateTerminatingFlag: 315 return BackendStateTerminating 316 case BackendStateQuarantinedFlag: 317 return BackendStateQuarantined 318 case BackendStateMaintenanceFlag: 319 return BackendStateMaintenance 320 default: 321 return BackendStateActive 322 } 323 } 324 325 // DefaultBackendWeight is used when backend weight is not set in ServiceSpec 326 const DefaultBackendWeight = 100 327 328 var ( 329 // AllProtocols is the list of all supported L4 protocols 330 AllProtocols = []L4Type{TCP, UDP, SCTP} 331 ) 332 333 // L4Type name. 334 type L4Type = string 335 336 // FEPortName is the name of the frontend's port. 337 type FEPortName string 338 339 // ServiceID is the service's ID. 340 type ServiceID uint16 341 342 // ServiceName represents the fully-qualified reference to the service by name, 343 // including both the namespace and name of the service (and optionally the cluster). 344 type ServiceName struct { 345 Namespace string 346 Name string 347 Cluster string 348 } 349 350 func (n ServiceName) String() string { 351 if n.Cluster != "" { 352 return n.Cluster + "/" + n.Namespace + "/" + n.Name 353 } 354 355 return n.Namespace + "/" + n.Name 356 } 357 358 // BackendID is the backend's ID. 359 type BackendID uint32 360 361 // ID is the ID of L3n4Addr endpoint (either service or backend). 362 type ID uint32 363 364 // BackendState is the state of a backend for load-balancing service traffic. 365 type BackendState uint8 366 367 // Preferred indicates if this backend is preferred to be load balanced. 368 type Preferred bool 369 370 // Backend represents load balancer backend. 371 type Backend struct { 372 // FEPortName is the frontend port name. This is used to filter backends sending to EDS. 373 FEPortName string 374 // ID of the backend 375 ID BackendID 376 // Weight of backend 377 Weight uint16 378 // Node hosting this backend. This is used to determine backends local to 379 // a node. 380 NodeName string 381 // Zone where backend is located. 382 ZoneID uint8 383 L3n4Addr 384 // State of the backend for load-balancing service traffic 385 State BackendState 386 // Preferred indicates if the healthy backend is preferred 387 Preferred Preferred 388 } 389 390 func (b *Backend) String() string { 391 return b.L3n4Addr.String() 392 } 393 394 // SVC is a structure for storing service details. 395 type SVC struct { 396 Frontend L3n4AddrID // SVC frontend addr and an allocated ID 397 Backends []*Backend // List of service backends 398 Type SVCType // Service type 399 ExtTrafficPolicy SVCTrafficPolicy // Service external traffic policy 400 IntTrafficPolicy SVCTrafficPolicy // Service internal traffic policy 401 NatPolicy SVCNatPolicy // Service NAT 46/64 policy 402 SessionAffinity bool 403 SessionAffinityTimeoutSec uint32 404 HealthCheckNodePort uint16 // Service health check node port 405 Name ServiceName // Fully qualified service name 406 LoadBalancerSourceRanges []*cidr.CIDR 407 L7LBProxyPort uint16 // Non-zero for L7 LB services 408 LoopbackHostport bool 409 } 410 411 func (s *SVC) GetModel() *models.Service { 412 var natPolicy string 413 type backendPlacement struct { 414 pos int 415 id BackendID 416 } 417 418 if s == nil { 419 return nil 420 } 421 422 id := int64(s.Frontend.ID) 423 if s.NatPolicy != SVCNatPolicyNone { 424 natPolicy = string(s.NatPolicy) 425 } 426 spec := &models.ServiceSpec{ 427 ID: id, 428 FrontendAddress: s.Frontend.GetModel(), 429 BackendAddresses: make([]*models.BackendAddress, len(s.Backends)), 430 Flags: &models.ServiceSpecFlags{ 431 Type: string(s.Type), 432 TrafficPolicy: string(s.ExtTrafficPolicy), 433 ExtTrafficPolicy: string(s.ExtTrafficPolicy), 434 IntTrafficPolicy: string(s.IntTrafficPolicy), 435 NatPolicy: natPolicy, 436 HealthCheckNodePort: s.HealthCheckNodePort, 437 438 Name: s.Name.Name, 439 Namespace: s.Name.Namespace, 440 }, 441 } 442 443 if s.Name.Cluster != option.Config.ClusterName { 444 spec.Flags.Cluster = s.Name.Cluster 445 } 446 447 placements := make([]backendPlacement, len(s.Backends)) 448 for i, be := range s.Backends { 449 placements[i] = backendPlacement{pos: i, id: be.ID} 450 } 451 sort.Slice(placements, 452 func(i, j int) bool { return placements[i].id < placements[j].id }) 453 for i, placement := range placements { 454 spec.BackendAddresses[i] = s.Backends[placement.pos].GetBackendModel() 455 } 456 457 return &models.Service{ 458 Spec: spec, 459 Status: &models.ServiceStatus{ 460 Realized: spec, 461 }, 462 } 463 } 464 465 func IsValidStateTransition(old, new BackendState) bool { 466 if old == new { 467 return true 468 } 469 if new == BackendStateInvalid { 470 return false 471 } 472 473 switch old { 474 case BackendStateActive: 475 case BackendStateTerminating: 476 return false 477 case BackendStateQuarantined: 478 if new == BackendStateMaintenance { 479 return false 480 } 481 case BackendStateMaintenance: 482 if new != BackendStateActive { 483 return false 484 } 485 default: 486 return false 487 } 488 return true 489 } 490 491 func GetBackendState(state string) (BackendState, error) { 492 switch strings.ToLower(state) { 493 case models.BackendAddressStateActive, "": 494 return BackendStateActive, nil 495 case models.BackendAddressStateTerminating: 496 return BackendStateTerminating, nil 497 case models.BackendAddressStateQuarantined: 498 return BackendStateQuarantined, nil 499 case models.BackendAddressStateMaintenance: 500 return BackendStateMaintenance, nil 501 default: 502 return BackendStateInvalid, fmt.Errorf("invalid backend state %s", state) 503 } 504 } 505 506 func (state BackendState) String() (string, error) { 507 switch state { 508 case BackendStateActive: 509 return models.BackendAddressStateActive, nil 510 case BackendStateTerminating: 511 return models.BackendAddressStateTerminating, nil 512 case BackendStateQuarantined: 513 return models.BackendAddressStateQuarantined, nil 514 case BackendStateMaintenance: 515 return models.BackendAddressStateMaintenance, nil 516 default: 517 return "", fmt.Errorf("invalid backend state %d", state) 518 } 519 } 520 521 func IsValidBackendState(state string) bool { 522 _, err := GetBackendState(state) 523 524 return err == nil 525 } 526 527 func NewL4Type(name string) (L4Type, error) { 528 switch strings.ToLower(name) { 529 case "tcp": 530 return TCP, nil 531 case "udp": 532 return UDP, nil 533 case "sctp": 534 return SCTP, nil 535 default: 536 return "", fmt.Errorf("unknown L4 protocol") 537 } 538 } 539 540 // L4Addr is an abstraction for the backend port with a L4Type, usually tcp or udp, and 541 // the Port number. 542 // 543 // +deepequal-gen=true 544 // +deepequal-gen:private-method=true 545 type L4Addr struct { 546 Protocol L4Type 547 Port uint16 548 } 549 550 // DeepEqual returns true if both the receiver and 'o' are deeply equal. 551 func (l *L4Addr) DeepEqual(o *L4Addr) bool { 552 if l == nil { 553 return o == nil 554 } 555 return l.deepEqual(o) 556 } 557 558 // NewL4Addr creates a new L4Addr. 559 func NewL4Addr(protocol L4Type, number uint16) *L4Addr { 560 return &L4Addr{Protocol: protocol, Port: number} 561 } 562 563 // L3n4Addr is used to store, as an unique L3+L4 address in the KVStore. It also 564 // includes the lookup scope for frontend addresses which is used in service 565 // handling for externalTrafficPolicy=Local and internalTrafficPolicy=Local, 566 // that is, Scope{External,Internal}. 567 // 568 // +deepequal-gen=true 569 // +deepequal-gen:private-method=true 570 type L3n4Addr struct { 571 AddrCluster cmtypes.AddrCluster 572 L4Addr 573 Scope uint8 574 } 575 576 // DeepEqual returns true if both the receiver and 'o' are deeply equal. 577 func (l *L3n4Addr) DeepEqual(o *L3n4Addr) bool { 578 if l == nil { 579 return o == nil 580 } 581 return l.AddrCluster.Equal(o.AddrCluster) && l.deepEqual(o) 582 } 583 584 // NewL3n4Addr creates a new L3n4Addr. 585 func NewL3n4Addr(protocol L4Type, addrCluster cmtypes.AddrCluster, portNumber uint16, scope uint8) *L3n4Addr { 586 lbport := NewL4Addr(protocol, portNumber) 587 588 addr := L3n4Addr{AddrCluster: addrCluster, L4Addr: *lbport, Scope: scope} 589 590 return &addr 591 } 592 593 func NewL3n4AddrFromModel(base *models.FrontendAddress) (*L3n4Addr, error) { 594 var scope uint8 595 596 if base == nil { 597 return nil, nil 598 } 599 600 if base.IP == "" { 601 return nil, fmt.Errorf("missing IP address") 602 } 603 604 proto := NONE 605 if base.Protocol != "" { 606 p, err := NewL4Type(base.Protocol) 607 if err != nil { 608 return nil, err 609 } 610 proto = p 611 } 612 613 l4addr := NewL4Addr(proto, base.Port) 614 addrCluster, err := cmtypes.ParseAddrCluster(base.IP) 615 if err != nil { 616 return nil, err 617 } 618 619 if base.Scope == models.FrontendAddressScopeExternal { 620 scope = ScopeExternal 621 } else if base.Scope == models.FrontendAddressScopeInternal { 622 scope = ScopeInternal 623 } else { 624 return nil, fmt.Errorf("invalid scope \"%s\"", base.Scope) 625 } 626 627 return &L3n4Addr{AddrCluster: addrCluster, L4Addr: *l4addr, Scope: scope}, nil 628 } 629 630 // NewBackend creates the Backend struct instance from given params. 631 // The default state for the returned Backend is BackendStateActive. 632 func NewBackend(id BackendID, protocol L4Type, addrCluster cmtypes.AddrCluster, portNumber uint16) *Backend { 633 return NewBackendWithState(id, protocol, addrCluster, portNumber, 0, BackendStateActive) 634 } 635 636 // NewBackendWithState creates the Backend struct instance from given params. 637 func NewBackendWithState(id BackendID, protocol L4Type, addrCluster cmtypes.AddrCluster, portNumber uint16, zone uint8, 638 state BackendState) *Backend { 639 lbport := NewL4Addr(protocol, portNumber) 640 b := Backend{ 641 ID: id, 642 L3n4Addr: L3n4Addr{AddrCluster: addrCluster, L4Addr: *lbport}, 643 State: state, 644 Weight: DefaultBackendWeight, 645 ZoneID: zone, 646 } 647 648 return &b 649 } 650 651 func NewBackendFromBackendModel(base *models.BackendAddress) (*Backend, error) { 652 if base.IP == nil { 653 return nil, fmt.Errorf("missing IP address") 654 } 655 656 // FIXME: Should this be NONE ? 657 l4addr := NewL4Addr(NONE, base.Port) 658 addrCluster, err := cmtypes.ParseAddrCluster(*base.IP) 659 if err != nil { 660 return nil, err 661 } 662 state, err := GetBackendState(base.State) 663 if err != nil { 664 return nil, fmt.Errorf("invalid backend state [%s]", base.State) 665 } 666 667 b := &Backend{ 668 NodeName: base.NodeName, 669 ZoneID: option.Config.GetZoneID(base.Zone), 670 L3n4Addr: L3n4Addr{AddrCluster: addrCluster, L4Addr: *l4addr}, 671 State: state, 672 Preferred: Preferred(base.Preferred), 673 } 674 675 if base.Weight != nil { 676 b.Weight = *base.Weight 677 } 678 679 if b.Weight == 0 { 680 b.State = BackendStateMaintenance 681 } 682 683 return b, nil 684 } 685 686 func NewL3n4AddrFromBackendModel(base *models.BackendAddress) (*L3n4Addr, error) { 687 if base.IP == nil { 688 return nil, fmt.Errorf("missing IP address") 689 } 690 691 // FIXME: Should this be NONE ? 692 l4addr := NewL4Addr(NONE, base.Port) 693 addrCluster, err := cmtypes.ParseAddrCluster(*base.IP) 694 if err != nil { 695 return nil, err 696 } 697 return &L3n4Addr{AddrCluster: addrCluster, L4Addr: *l4addr}, nil 698 } 699 700 func (a *L3n4Addr) GetModel() *models.FrontendAddress { 701 if a == nil { 702 return nil 703 } 704 705 scope := models.FrontendAddressScopeExternal 706 if a.Scope == ScopeInternal { 707 scope = models.FrontendAddressScopeInternal 708 } 709 return &models.FrontendAddress{ 710 IP: a.AddrCluster.String(), 711 Port: a.Port, 712 Scope: scope, 713 } 714 } 715 716 func (b *Backend) GetBackendModel() *models.BackendAddress { 717 if b == nil { 718 return nil 719 } 720 721 addrClusterStr := b.AddrCluster.String() 722 stateStr, _ := b.State.String() 723 return &models.BackendAddress{ 724 IP: &addrClusterStr, 725 Port: b.Port, 726 NodeName: b.NodeName, 727 Zone: option.Config.GetZone(b.ZoneID), 728 State: stateStr, 729 Preferred: bool(b.Preferred), 730 Weight: &b.Weight, 731 } 732 } 733 734 // String returns the L3n4Addr in the "IPv4:Port[/Scope]" format for IPv4 and 735 // "[IPv6]:Port[/Scope]" format for IPv6. 736 func (a *L3n4Addr) String() string { 737 var scope string 738 if a.Scope == ScopeInternal { 739 scope = "/i" 740 } 741 if a.IsIPv6() { 742 return "[" + a.AddrCluster.String() + "]:" + strconv.FormatUint(uint64(a.Port), 10) + scope 743 } 744 return a.AddrCluster.String() + ":" + strconv.FormatUint(uint64(a.Port), 10) + scope 745 } 746 747 // StringWithProtocol returns the L3n4Addr in the "IPv4:Port/Protocol[/Scope]" 748 // format for IPv4 and "[IPv6]:Port/Protocol[/Scope]" format for IPv6. 749 func (a *L3n4Addr) StringWithProtocol() string { 750 var scope string 751 if a.Scope == ScopeInternal { 752 scope = "/i" 753 } 754 if a.IsIPv6() { 755 return "[" + a.AddrCluster.String() + "]:" + strconv.FormatUint(uint64(a.Port), 10) + "/" + a.Protocol + scope 756 } 757 return a.AddrCluster.String() + ":" + strconv.FormatUint(uint64(a.Port), 10) + "/" + a.Protocol + scope 758 } 759 760 // StringID returns the L3n4Addr as string to be used for unique identification 761 func (a *L3n4Addr) StringID() string { 762 // This does not include the protocol right now as the datapath does 763 // not include the protocol in the lookup of the service IP. 764 return a.String() 765 } 766 767 // Hash calculates a unique string of the L3n4Addr e.g for use as a key in maps. 768 // Note: the resulting string is meant to be used as a key for maps and is not 769 // readable by a human eye when printed out. 770 func (a L3n4Addr) Hash() string { 771 const lenProto = 0 // proto is omitted for now 772 const lenScope = 1 // scope is uint8 which is an alias for byte 773 const lenPort = 2 // port is uint16 which is 2 bytes 774 775 b := make([]byte, cmtypes.AddrClusterLen+lenProto+lenScope+lenPort) 776 ac20 := a.AddrCluster.As20() 777 copy(b, ac20[:]) 778 // FIXME: add Protocol once we care about protocols 779 // scope is a uint8 which is an alias for byte so a cast is safe 780 b[net.IPv6len+lenProto] = byte(a.Scope) 781 // port is a uint16, so 2 bytes 782 b[net.IPv6len+lenProto+lenScope] = byte(a.Port >> 8) 783 b[net.IPv6len+lenProto+lenScope+1] = byte(a.Port & 0xff) 784 return string(b) 785 } 786 787 // IsIPv6 returns true if the IP address in the given L3n4Addr is IPv6 or not. 788 func (a *L3n4Addr) IsIPv6() bool { 789 return a.AddrCluster.Is6() 790 } 791 792 // L3n4AddrID is used to store, as an unique L3+L4 plus the assigned ID, in the 793 // KVStore. 794 // 795 // +deepequal-gen=true 796 // +deepequal-gen:private-method=true 797 type L3n4AddrID struct { 798 L3n4Addr 799 ID ID 800 } 801 802 // DeepEqual returns true if both the receiver and 'o' are deeply equal. 803 func (l *L3n4AddrID) DeepEqual(o *L3n4AddrID) bool { 804 if l == nil { 805 return o == nil 806 } 807 return l.deepEqual(o) 808 } 809 810 // NewL3n4AddrID creates a new L3n4AddrID. 811 func NewL3n4AddrID(protocol L4Type, addrCluster cmtypes.AddrCluster, portNumber uint16, scope uint8, id ID) *L3n4AddrID { 812 l3n4Addr := NewL3n4Addr(protocol, addrCluster, portNumber, scope) 813 return &L3n4AddrID{L3n4Addr: *l3n4Addr, ID: id} 814 } 815 816 // IsIPv6 returns true if the IP address in L3n4Addr's L3n4AddrID is IPv6 or not. 817 func (l *L3n4AddrID) IsIPv6() bool { 818 return l.L3n4Addr.IsIPv6() 819 }