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 }