github.com/cilium/cilium@v1.16.2/pkg/clustermesh/types/addressing.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package types
     5  
     6  import (
     7  	"fmt"
     8  	"net"
     9  	"net/netip"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"go4.org/netipx"
    14  
    15  	"github.com/cilium/cilium/pkg/cidr"
    16  	ippkg "github.com/cilium/cilium/pkg/ip"
    17  )
    18  
    19  //
    20  // In this file, we define types and utilities for cluster-aware
    21  // addressing which identifies network endpoints using IP address
    22  // and an optional ClusterID. With this special addressing scheme,
    23  // we can distinguish network endpoints (e.g. Pods) that have the
    24  // same IP address, but belong to the different cluster.
    25  //
    26  // A "bare" IP address is still a valid identifier because there
    27  // are cases that endpoints can be identified without ClusterID
    28  // (e.g. network endpoint has a unique IP address). We can consider
    29  // this as a special case that ClusterID "doesn't matter". ClusterID
    30  // 0 is reserved for indicating that.
    31  //
    32  
    33  // AddrCluster is a type that holds a pair of IP and ClusterID.
    34  // We should use this type as much as possible when we implement
    35  // IP + Cluster addressing. We should avoid managing IP and ClusterID
    36  // separately. Otherwise, it is very hard for code readers to see
    37  // where we are using cluster-aware addressing.
    38  type AddrCluster struct {
    39  	addr      netip.Addr
    40  	clusterID uint32
    41  }
    42  
    43  const AddrClusterLen = 20
    44  
    45  // ParseAddrCluster parses s as an IP + ClusterID and returns AddrCluster.
    46  // The string s can be a bare IP string (any IP address format allowed in
    47  // netip.ParseAddr()) or IP string + @ + ClusterID with decimal. Bare IP
    48  // string is considered as IP string + @ + ClusterID = 0.
    49  func ParseAddrCluster(s string) (AddrCluster, error) {
    50  	atIndex := strings.LastIndex(s, "@")
    51  
    52  	var (
    53  		addrStr      string
    54  		clusterIDStr string
    55  	)
    56  
    57  	if atIndex == -1 {
    58  		// s may be a bare IP address string, still valid
    59  		addrStr = s
    60  		clusterIDStr = ""
    61  	} else {
    62  		// s may be a IP + ClusterID string
    63  		addrStr = s[:atIndex]
    64  		clusterIDStr = s[atIndex+1:]
    65  	}
    66  
    67  	addr, err := netip.ParseAddr(addrStr)
    68  	if err != nil {
    69  		return AddrCluster{}, err
    70  	}
    71  
    72  	if clusterIDStr == "" {
    73  		if atIndex != len(s)-1 {
    74  			return AddrCluster{addr: addr, clusterID: 0}, nil
    75  		} else {
    76  			// handle the invalid case like "10.0.0.0@"
    77  			return AddrCluster{}, fmt.Errorf("empty cluster ID")
    78  		}
    79  	}
    80  
    81  	clusterID64, err := strconv.ParseUint(clusterIDStr, 10, 32)
    82  	if err != nil {
    83  		return AddrCluster{}, err
    84  	}
    85  
    86  	return AddrCluster{addr: addr, clusterID: uint32(clusterID64)}, nil
    87  }
    88  
    89  // MustParseAddrCluster calls ParseAddr(s) and panics on error. It is
    90  // intended for use in tests with hard-coded strings.
    91  func MustParseAddrCluster(s string) AddrCluster {
    92  	addrCluster, err := ParseAddrCluster(s)
    93  	if err != nil {
    94  		panic(err)
    95  	}
    96  	return addrCluster
    97  }
    98  
    99  // AddrClusterFromIP parses the given net.IP using ip.AddrFromIP and returns
   100  // AddrCluster with ClusterID = 0.
   101  func AddrClusterFromIP(ip net.IP) (AddrCluster, bool) {
   102  	addr, ok := ippkg.AddrFromIP(ip)
   103  	if !ok {
   104  		return AddrCluster{}, false
   105  	}
   106  	return AddrCluster{addr: addr, clusterID: 0}, true
   107  }
   108  
   109  func MustAddrClusterFromIP(ip net.IP) AddrCluster {
   110  	addr, ok := AddrClusterFromIP(ip)
   111  	if !ok {
   112  		panic("cannot convert net.IP to AddrCluster")
   113  	}
   114  	return addr
   115  }
   116  
   117  // AddrClusterFrom creates AddrCluster from netip.Addr and ClusterID
   118  func AddrClusterFrom(addr netip.Addr, clusterID uint32) AddrCluster {
   119  	return AddrCluster{addr: addr, clusterID: clusterID}
   120  }
   121  
   122  // Addr returns IP address part of AddrCluster as netip.Addr. This function
   123  // exists for keeping backward compatibility between the existing components
   124  // which are not aware of the cluster-aware addressing. Calling this function
   125  // against the AddrCluster which has non-zero clusterID will lose the ClusterID
   126  // information. It should be used with an extra care.
   127  func (ac AddrCluster) Addr() netip.Addr {
   128  	return ac.addr
   129  }
   130  
   131  // ClusterID returns ClusterID part of AddrCluster as uint32. We should avoid
   132  // using this function as much as possible and treat IP address and ClusterID
   133  // together.
   134  func (ac AddrCluster) ClusterID() uint32 {
   135  	return ac.clusterID
   136  }
   137  
   138  // Equal returns true when given AddrCluster has a same IP address and ClusterID
   139  func (ac0 AddrCluster) Equal(ac1 AddrCluster) bool {
   140  	return ac0.addr == ac1.addr && ac0.clusterID == ac1.clusterID
   141  }
   142  
   143  // Less compares ac0 and ac1 and returns true if ac0 is lesser than ac1
   144  func (ac0 AddrCluster) Less(ac1 AddrCluster) bool {
   145  	// First, compare the IP address part
   146  	if ret := ac0.addr.Compare(ac1.addr); ret == -1 {
   147  		return true
   148  	} else if ret == 1 {
   149  		return false
   150  	} else {
   151  		// If IP address is the same, compare ClusterID
   152  		return ac0.clusterID < ac1.clusterID
   153  	}
   154  }
   155  
   156  // This is an alias of Equal which only exists for satisfying deepequal-gen
   157  func (ac0 *AddrCluster) DeepEqual(ac1 *AddrCluster) bool {
   158  	return ac0.Equal(*ac1)
   159  }
   160  
   161  // DeepCopyInto copies in to out
   162  func (in *AddrCluster) DeepCopyInto(out *AddrCluster) {
   163  	if out == nil {
   164  		return
   165  	}
   166  	out.addr = in.addr
   167  	out.clusterID = in.clusterID
   168  }
   169  
   170  // DeepCopy returns a new copy of AddrCluster
   171  func (in *AddrCluster) DeepCopy() *AddrCluster {
   172  	out := new(AddrCluster)
   173  	in.DeepCopyInto(out)
   174  	return out
   175  }
   176  
   177  // String returns the string representation of the AddrCluster. If
   178  // AddrCluster.clusterID = 0, it returns bare IP address string. Otherwise, it
   179  // returns IP string + "@" + ClusterID (e.g. 10.0.0.1@1)
   180  func (ac AddrCluster) String() string {
   181  	if ac.clusterID == 0 {
   182  		return ac.addr.String()
   183  	}
   184  	return ac.addr.String() + "@" + strconv.FormatUint(uint64(ac.clusterID), 10)
   185  }
   186  
   187  // Is4 reports whether IP address part of AddrCluster is an IPv4 address.
   188  func (ac AddrCluster) Is4() bool {
   189  	return ac.addr.Is4()
   190  }
   191  
   192  // Is6 reports whether IP address part of AddrCluster is an IPv6 address.
   193  func (ac AddrCluster) Is6() bool {
   194  	return ac.addr.Is6()
   195  }
   196  
   197  // IsUnspecified reports whether IP address part of the AddrCluster is an
   198  // unspecified address, either the IPv4 address "0.0.0.0" or the IPv6
   199  // address "::".
   200  func (ac AddrCluster) IsUnspecified() bool {
   201  	return ac.addr.IsUnspecified()
   202  }
   203  
   204  // As20 returns the AddrCluster in its 20-byte representation which consists
   205  // of 16-byte IP address part from netip.Addr.As16 and 4-byte ClusterID part.
   206  func (ac AddrCluster) As20() (ac20 [20]byte) {
   207  	addr16 := ac.addr.As16()
   208  	copy(ac20[:16], addr16[:])
   209  	ac20[16] = byte(ac.clusterID >> 24)
   210  	ac20[17] = byte(ac.clusterID >> 16)
   211  	ac20[18] = byte(ac.clusterID >> 8)
   212  	ac20[19] = byte(ac.clusterID)
   213  	return ac20
   214  }
   215  
   216  // AsNetIP returns the IP address part of AddCluster as a net.IP type. This
   217  // function exists for keeping backward compatibility between the existing
   218  // components which are not aware of the cluster-aware addressing. Calling
   219  // this function against the AddrCluster which has non-zero clusterID will
   220  // lose the ClusterID information. It should be used with an extra care.
   221  func (ac AddrCluster) AsNetIP() net.IP {
   222  	return ac.addr.AsSlice()
   223  }
   224  
   225  func (ac AddrCluster) AsPrefixCluster() PrefixCluster {
   226  	return PrefixClusterFrom(ac.addr, ac.addr.BitLen(), WithClusterID(ac.clusterID))
   227  }
   228  
   229  // PrefixCluster is a type that holds a pair of prefix and ClusterID.
   230  // We should use this type as much as possible when we implement
   231  // prefix + Cluster addressing. We should avoid managing prefix and
   232  // ClusterID separately. Otherwise, it is very hard for code readers
   233  // to see where we are using cluster-aware addressing.
   234  type PrefixCluster struct {
   235  	prefix    netip.Prefix
   236  	clusterID uint32
   237  }
   238  
   239  // ParsePrefixCluster parses s as an Prefix + ClusterID and returns PrefixCluster.
   240  // The string s can be a bare IP prefix string (any prefix format allowed in
   241  // netip.ParsePrefix()) or prefix string + @ + ClusterID with decimal. Bare prefix
   242  // string is considered as prefix string + @ + ClusterID = 0.
   243  func ParsePrefixCluster(s string) (PrefixCluster, error) {
   244  	atIndex := strings.LastIndex(s, "@")
   245  
   246  	var (
   247  		prefixStr    string
   248  		clusterIDStr string
   249  	)
   250  
   251  	if atIndex == -1 {
   252  		// s may be a bare IP prefix string, still valid
   253  		prefixStr = s
   254  		clusterIDStr = ""
   255  	} else {
   256  		// s may be a prefix + ClusterID string
   257  		prefixStr = s[:atIndex]
   258  		clusterIDStr = s[atIndex+1:]
   259  	}
   260  
   261  	prefix, err := netip.ParsePrefix(prefixStr)
   262  	if err != nil {
   263  		return PrefixCluster{}, err
   264  	}
   265  
   266  	if clusterIDStr == "" {
   267  		if atIndex != len(s)-1 {
   268  			return PrefixCluster{prefix: prefix, clusterID: 0}, nil
   269  		} else {
   270  			// handle the invalid case like "10.0.0.0/24@"
   271  			return PrefixCluster{}, fmt.Errorf("empty cluster ID")
   272  		}
   273  	}
   274  
   275  	clusterID64, err := strconv.ParseUint(clusterIDStr, 10, 32)
   276  	if err != nil {
   277  		return PrefixCluster{}, err
   278  	}
   279  
   280  	return PrefixCluster{prefix: prefix, clusterID: uint32(clusterID64)}, nil
   281  }
   282  
   283  // MustParsePrefixCluster calls ParsePrefixCluster(s) and panics on error.
   284  // It is intended for use in tests with hard-coded strings.
   285  func MustParsePrefixCluster(s string) PrefixCluster {
   286  	prefixCluster, err := ParsePrefixCluster(s)
   287  	if err != nil {
   288  		panic(err)
   289  	}
   290  	return prefixCluster
   291  }
   292  
   293  func (pc PrefixCluster) IsSingleIP() bool {
   294  	return pc.prefix.IsSingleIP()
   295  }
   296  
   297  type PrefixClusterOpts func(*PrefixCluster)
   298  
   299  func WithClusterID(id uint32) PrefixClusterOpts {
   300  	return func(pc *PrefixCluster) { pc.clusterID = id }
   301  }
   302  
   303  func PrefixClusterFrom(addr netip.Addr, bits int, opts ...PrefixClusterOpts) PrefixCluster {
   304  	pc := PrefixCluster{prefix: netip.PrefixFrom(addr, bits)}
   305  	for _, opt := range opts {
   306  		opt(&pc)
   307  	}
   308  	return pc
   309  }
   310  
   311  func PrefixClusterFromCIDR(c *cidr.CIDR, opts ...PrefixClusterOpts) PrefixCluster {
   312  	if c == nil {
   313  		return PrefixCluster{}
   314  	}
   315  
   316  	addr, ok := ippkg.AddrFromIP(c.IP)
   317  	if !ok {
   318  		return PrefixCluster{}
   319  	}
   320  	ones, _ := c.Mask.Size()
   321  
   322  	return PrefixClusterFrom(addr, ones, opts...)
   323  }
   324  
   325  func (pc0 PrefixCluster) Equal(pc1 PrefixCluster) bool {
   326  	return pc0.prefix == pc1.prefix && pc0.clusterID == pc1.clusterID
   327  }
   328  
   329  func (pc PrefixCluster) IsValid() bool {
   330  	return pc.prefix.IsValid()
   331  }
   332  
   333  func (pc PrefixCluster) AddrCluster() AddrCluster {
   334  	return AddrClusterFrom(pc.prefix.Addr(), pc.clusterID)
   335  }
   336  
   337  func (pc PrefixCluster) ClusterID() uint32 {
   338  	return pc.clusterID
   339  }
   340  
   341  func (pc PrefixCluster) String() string {
   342  	if pc.clusterID == 0 {
   343  		return pc.prefix.String()
   344  	}
   345  	return pc.prefix.String() + "@" + strconv.FormatUint(uint64(pc.clusterID), 10)
   346  }
   347  
   348  // AsPrefix returns the IP prefix part of PrefixCluster as a netip.Prefix type.
   349  // This function exists for keeping backward compatibility between the existing
   350  // components which are not aware of the cluster-aware addressing. Calling
   351  // this function against the PrefixCluster which has non-zero clusterID will
   352  // lose the ClusterID information. It should be used with an extra care.
   353  func (pc PrefixCluster) AsPrefix() netip.Prefix {
   354  	return netip.PrefixFrom(pc.prefix.Addr(), pc.prefix.Bits())
   355  }
   356  
   357  // AsIPNet returns the IP prefix part of PrefixCluster as a net.IPNet type. This
   358  // function exists for keeping backward compatibility between the existing
   359  // components which are not aware of the cluster-aware addressing. Calling
   360  // this function against the PrefixCluster which has non-zero clusterID will
   361  // lose the ClusterID information. It should be used with an extra care.
   362  func (pc PrefixCluster) AsIPNet() net.IPNet {
   363  	return *netipx.PrefixIPNet(pc.AsPrefix())
   364  }
   365  
   366  // This function is solely exists for annotating IPCache's key string with ClusterID.
   367  // IPCache's key string is IP address or Prefix string (10.0.0.1 and 10.0.0.0/32 are
   368  // different entry). This function assumes given string is one of those format and
   369  // just put @<ClusterID> suffix and there's no format check for performance reason.
   370  // User must make sure the input is a valid IP or Prefix string.
   371  //
   372  // We should eventually remove this function once we finish refactoring IPCache and
   373  // stop using string as a key. At that point, we should consider using PrefixCluster
   374  // type for IPCache's key.
   375  func AnnotateIPCacheKeyWithClusterID(key string, clusterID uint32) string {
   376  	return key + "@" + strconv.FormatUint(uint64(clusterID), 10)
   377  }