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  }