github.com/cilium/cilium@v1.16.2/pkg/labels/cidr.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package labels
     5  
     6  import (
     7  	"fmt"
     8  	"net/netip"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/cilium/cilium/pkg/option"
    13  )
    14  
    15  var (
    16  	worldLabelNonDualStack = Label{Key: IDNameWorld, Source: LabelSourceReserved}
    17  	worldLabelV4           = Label{Source: LabelSourceReserved, Key: IDNameWorldIPv4}
    18  	worldLabelV6           = Label{Source: LabelSourceReserved, Key: IDNameWorldIPv6}
    19  )
    20  
    21  // maskedIPToLabelString is the base method for serializing an IP + prefix into
    22  // a string that can be used for creating Labels and EndpointSelector objects.
    23  //
    24  // For IPv6 addresses, it converts ":" into "-" as EndpointSelectors don't
    25  // support colons inside the name section of a label.
    26  func maskedIPToLabel(ipStr string, prefix int) Label {
    27  	var str strings.Builder
    28  	str.Grow(
    29  		1 /* preZero */ +
    30  			len(ipStr) +
    31  			1 /* postZero */ +
    32  			2 /*len of prefix*/ +
    33  			1, /* '/' */
    34  	)
    35  
    36  	for i := 0; i < len(ipStr); i++ {
    37  		if ipStr[i] == ':' {
    38  			// EndpointSelector keys can't start or end with a "-", so insert a
    39  			// zero at the start or end if it would otherwise have a "-" at that
    40  			// position.
    41  			if i == 0 {
    42  				str.WriteByte('0')
    43  				str.WriteByte('-')
    44  				continue
    45  			}
    46  			if i == len(ipStr)-1 {
    47  				str.WriteByte('-')
    48  				str.WriteByte('0')
    49  				continue
    50  			}
    51  			str.WriteByte('-')
    52  		} else {
    53  			str.WriteByte(ipStr[i])
    54  		}
    55  	}
    56  	str.WriteRune('/')
    57  	str.WriteString(strconv.Itoa(prefix))
    58  	return Label{Key: str.String(), Source: LabelSourceCIDR}
    59  }
    60  
    61  // IPStringToLabel parses a string and returns it as a CIDR label.
    62  //
    63  // If ip is not a valid IP address or CIDR Prefix, returns an error.
    64  func IPStringToLabel(ip string) (Label, error) {
    65  	// factored out of netip.ParsePrefix to avoid allocating an empty netip.Prefix in case it's
    66  	// an IP and not a CIDR.
    67  	i := strings.LastIndexByte(ip, '/')
    68  	if i < 0 {
    69  		parsedIP, err := netip.ParseAddr(ip)
    70  		if err != nil {
    71  			return Label{}, fmt.Errorf("%q is not an IP address: %w", ip, err)
    72  		}
    73  		return maskedIPToLabel(ip, parsedIP.BitLen()), nil
    74  	} else {
    75  		parsedPrefix, err := netip.ParsePrefix(ip)
    76  		if err != nil {
    77  			return Label{}, fmt.Errorf("%q is not a CIDR: %w", ip, err)
    78  		}
    79  		return maskedIPToLabel(parsedPrefix.Masked().Addr().String(), parsedPrefix.Bits()), nil
    80  	}
    81  }
    82  
    83  // GetCIDRLabels turns a CIDR in to a specially formatted label, and returns
    84  // a Labels including the CIDR-specific label and the appropriate world label.
    85  // e.g. "10.0.0.0/8" => ["cidr:10.0.0.0/8", "reserved:world-ipv4"]
    86  //
    87  // IPv6 requires some special treatment, since ":" is special in the label selector
    88  // grammar. For example, "::/0" becomes "cidr:0--0/0",
    89  func GetCIDRLabels(prefix netip.Prefix) Labels {
    90  	lbls := make(Labels, 2)
    91  	if prefix.Bits() > 0 {
    92  		l := maskedIPToLabel(prefix.Addr().String(), prefix.Bits())
    93  		l.cidr = &prefix
    94  		lbls[l.Key] = l
    95  	}
    96  	AddWorldLabel(prefix.Addr(), lbls)
    97  
    98  	return lbls
    99  }
   100  
   101  func AddWorldLabel(addr netip.Addr, lbls Labels) {
   102  	switch {
   103  	case !option.Config.IsDualStack():
   104  		lbls[worldLabelNonDualStack.Key] = worldLabelNonDualStack
   105  	case addr.Is4():
   106  		lbls[worldLabelV4.Key] = worldLabelV4
   107  	default:
   108  		lbls[worldLabelV6.Key] = worldLabelV6
   109  	}
   110  }
   111  
   112  func LabelToPrefix(key string) (netip.Prefix, error) {
   113  	prefixStr := strings.Replace(key, "-", ":", -1)
   114  	pfx, err := netip.ParsePrefix(prefixStr)
   115  	if err != nil {
   116  		return netip.Prefix{}, fmt.Errorf("failed to parse label prefix %s: %w", key, err)
   117  	}
   118  	return pfx, nil
   119  }