@@ -35,14 +35,54 @@ const (
3535 IPFamilyUnknown IPFamily = ""
3636)
3737
38- // IsDualStackIPs returns true if:
39- // - all elements of ips are valid
40- // - at least one IP from each family (v4 and v6) is present
41- func IsDualStackIPs (ips []net.IP ) bool {
38+ type ipOrString interface {
39+ net.IP | string
40+ }
41+
42+ type cidrOrString interface {
43+ * net.IPNet | string
44+ }
45+
46+ // IPFamilyOf returns the IP family of val (or IPFamilyUnknown if val is nil or invalid).
47+ // IPv6-encoded IPv4 addresses (e.g., "::ffff:1.2.3.4") are considered IPv4. val can be a
48+ // net.IP or a string containing a single IP address.
49+ //
50+ // Note that "k8s.io/utils/net/v2".IPFamily intentionally has identical values to
51+ // "k8s.io/api/core/v1".IPFamily and "k8s.io/discovery/v1".AddressType, so you can cast
52+ // the return value of this function to those types.
53+ func IPFamilyOf [T ipOrString ](val T ) IPFamily {
54+ switch typedVal := interface {}(val ).(type ) {
55+ case net.IP :
56+ switch {
57+ case typedVal .To4 () != nil :
58+ return IPv4
59+ case typedVal .To16 () != nil :
60+ return IPv6
61+ }
62+ case string :
63+ return IPFamilyOf (ParseIPSloppy (typedVal ))
64+ }
65+
66+ return IPFamilyUnknown
67+ }
68+
69+ // IsIPv4 returns true if IPFamilyOf(val) is IPv4 (and false if it is IPv6 or invalid).
70+ func IsIPv4 [T ipOrString ](val T ) bool {
71+ return IPFamilyOf (val ) == IPv4
72+ }
73+
74+ // IsIPv6 returns true if IPFamilyOf(val) is IPv6 (and false if it is IPv4 or invalid).
75+ func IsIPv6 [T ipOrString ](val T ) bool {
76+ return IPFamilyOf (val ) == IPv6
77+ }
78+
79+ // IsDualStack returns true if vals contains at least one IPv4 address and at least one
80+ // IPv6 address (and no invalid values).
81+ func IsDualStack [T ipOrString ](vals []T ) bool {
4282 v4Found := false
4383 v6Found := false
44- for _ , ip := range ips {
45- switch IPFamilyOf (ip ) {
84+ for _ , val := range vals {
85+ switch IPFamilyOf (val ) {
4686 case IPv4 :
4787 v4Found = true
4888 case IPv6 :
@@ -55,29 +95,56 @@ func IsDualStackIPs(ips []net.IP) bool {
5595 return (v4Found && v6Found )
5696}
5797
58- // IsDualStackIPStrings returns true if:
59- // - all elements of ips can be parsed as IPs
60- // - at least one IP from each family (v4 and v6) is present
61- func IsDualStackIPStrings (ips []string ) bool {
62- parsedIPs := make ([]net.IP , 0 , len (ips ))
63- for _ , ip := range ips {
64- parsedIP := ParseIPSloppy (ip )
65- if parsedIP == nil {
66- return false
98+ // IsDualStackPair returns true if vals contains exactly 1 IPv4 address and 1 IPv6 address
99+ // (in either order).
100+ func IsDualStackPair [T ipOrString ](vals []T ) bool {
101+ return len (vals ) == 2 && IsDualStack (vals )
102+ }
103+
104+ // IPFamilyOfCIDR returns the IP family of val (or IPFamilyUnknown if val is nil or
105+ // invalid). IPv6-encoded IPv4 addresses (e.g., "::ffff:1.2.3.0/120") are considered IPv4.
106+ // val can be a *net.IPNet or a string containing a single CIDR value.
107+ //
108+ // Note that "k8s.io/utils/net/v2".IPFamily intentionally has identical values to
109+ // "k8s.io/api/core/v1".IPFamily and "k8s.io/discovery/v1".AddressType, so you can cast
110+ // the return value of this function to those types.
111+ func IPFamilyOfCIDR [T cidrOrString ](val T ) IPFamily {
112+ switch typedVal := interface {}(val ).(type ) {
113+ case * net.IPNet :
114+ if typedVal != nil {
115+ family := IPFamilyOf (typedVal .IP )
116+ // An IPv6 CIDR must have a 128-bit mask. An IPv4 CIDR must have a
117+ // 32- or 128-bit mask. (Any other mask length is invalid.)
118+ _ , masklen := typedVal .Mask .Size ()
119+ if masklen == 128 || (family == IPv4 && masklen == 32 ) {
120+ return family
121+ }
67122 }
68- parsedIPs = append (parsedIPs , parsedIP )
123+ case string :
124+ parsedIP , _ , _ := ParseCIDRSloppy (typedVal )
125+ return IPFamilyOf (parsedIP )
69126 }
70- return IsDualStackIPs (parsedIPs )
127+
128+ return IPFamilyUnknown
129+ }
130+
131+ // IsIPv4CIDR returns true if IPFamilyOfCIDR(val) is IPv4 (and false if it is IPv6 or invalid).
132+ func IsIPv4CIDR [T cidrOrString ](val T ) bool {
133+ return IPFamilyOfCIDR (val ) == IPv4
71134}
72135
73- // IsDualStackCIDRs returns true if:
74- // - all elements of cidrs are non-nil
75- // - at least one CIDR from each family (v4 and v6) is present
76- func IsDualStackCIDRs (cidrs []* net.IPNet ) bool {
136+ // IsIPv6CIDR returns true if IPFamilyOfCIDR(val) is IPv6 (and false if it is IPv4 or invalid).
137+ func IsIPv6CIDR [T cidrOrString ](val T ) bool {
138+ return IPFamilyOfCIDR (val ) == IPv6
139+ }
140+
141+ // IsDualStackCIDRs returns true if vals contains at least one IPv4 CIDR value and at
142+ // least one IPv6 CIDR value (and no invalid values).
143+ func IsDualStackCIDRs [T cidrOrString ](vals []T ) bool {
77144 v4Found := false
78145 v6Found := false
79- for _ , cidr := range cidrs {
80- switch IPFamilyOfCIDR (cidr ) {
146+ for _ , val := range vals {
147+ switch IPFamilyOfCIDR (val ) {
81148 case IPv4 :
82149 v4Found = true
83150 case IPv6 :
@@ -90,101 +157,24 @@ func IsDualStackCIDRs(cidrs []*net.IPNet) bool {
90157 return (v4Found && v6Found )
91158}
92159
93- // IsDualStackCIDRStrings returns if
94- // - all elements of cidrs can be parsed as CIDRs
95- // - at least one CIDR from each family (v4 and v6) is present
96- func IsDualStackCIDRStrings (cidrs []string ) bool {
97- parsedCIDRs , err := ParseCIDRs (cidrs )
98- if err != nil {
99- return false
100- }
101- return IsDualStackCIDRs (parsedCIDRs )
160+ // IsDualStackCIDRPair returns true if vals contains exactly 1 IPv4 CIDR value and 1 IPv6
161+ // CIDR value (in either order).
162+ func IsDualStackCIDRPair [T cidrOrString ](vals []T ) bool {
163+ return len (vals ) == 2 && IsDualStackCIDRs (vals )
102164}
103165
104- // IPFamilyOf returns the IP family of ip, or IPFamilyUnknown if it is invalid.
105- func IPFamilyOf (ip net.IP ) IPFamily {
106- switch {
107- case ip .To4 () != nil :
108- return IPv4
109- case ip .To16 () != nil :
166+ // OtherIPFamily returns the other IP family from ipFamily.
167+ //
168+ // Note that "k8s.io/utils/net/v2".IPFamily intentionally has identical values to
169+ // "k8s.io/api/core/v1".IPFamily and "k8s.io/discovery/v1".AddressType, so you can cast
170+ // the input/output values of this function between these types.
171+ func OtherIPFamily (ipFamily IPFamily ) IPFamily {
172+ switch ipFamily {
173+ case IPv4 :
110174 return IPv6
175+ case IPv6 :
176+ return IPv4
111177 default :
112178 return IPFamilyUnknown
113179 }
114180}
115-
116- // IPFamilyOfString returns the IP family of ip, or IPFamilyUnknown if ip cannot
117- // be parsed as an IP.
118- func IPFamilyOfString (ip string ) IPFamily {
119- return IPFamilyOf (ParseIPSloppy (ip ))
120- }
121-
122- // IPFamilyOfCIDR returns the IP family of cidr.
123- func IPFamilyOfCIDR (cidr * net.IPNet ) IPFamily {
124- if cidr != nil {
125- family := IPFamilyOf (cidr .IP )
126- // An IPv6 CIDR must have a 128-bit mask. An IPv4 CIDR must have a
127- // 32- or 128-bit mask. (Any other mask length is invalid.)
128- _ , masklen := cidr .Mask .Size ()
129- if masklen == 128 || (family == IPv4 && masklen == 32 ) {
130- return family
131- }
132- }
133- return IPFamilyUnknown
134- }
135-
136- // IPFamilyOfCIDRString returns the IP family of cidr.
137- func IPFamilyOfCIDRString (cidr string ) IPFamily {
138- ip , _ , _ := ParseCIDRSloppy (cidr )
139- return IPFamilyOf (ip )
140- }
141-
142- // IsIPv6 returns true if netIP is IPv6 (and false if it is IPv4, nil, or invalid).
143- func IsIPv6 (netIP net.IP ) bool {
144- return IPFamilyOf (netIP ) == IPv6
145- }
146-
147- // IsIPv6String returns true if ip contains a single IPv6 address and nothing else. It
148- // returns false if ip is an empty string, an IPv4 address, or anything else that is not a
149- // single IPv6 address.
150- func IsIPv6String (ip string ) bool {
151- return IPFamilyOfString (ip ) == IPv6
152- }
153-
154- // IsIPv6CIDR returns true if a cidr is a valid IPv6 CIDR. It returns false if cidr is
155- // nil or an IPv4 CIDR. Its behavior is not defined if cidr is invalid.
156- func IsIPv6CIDR (cidr * net.IPNet ) bool {
157- return IPFamilyOfCIDR (cidr ) == IPv6
158- }
159-
160- // IsIPv6CIDRString returns true if cidr contains a single IPv6 CIDR and nothing else. It
161- // returns false if cidr is an empty string, an IPv4 CIDR, or anything else that is not a
162- // single valid IPv6 CIDR.
163- func IsIPv6CIDRString (cidr string ) bool {
164- return IPFamilyOfCIDRString (cidr ) == IPv6
165- }
166-
167- // IsIPv4 returns true if netIP is IPv4 (and false if it is IPv6, nil, or invalid).
168- func IsIPv4 (netIP net.IP ) bool {
169- return IPFamilyOf (netIP ) == IPv4
170- }
171-
172- // IsIPv4String returns true if ip contains a single IPv4 address and nothing else. It
173- // returns false if ip is an empty string, an IPv6 address, or anything else that is not a
174- // single IPv4 address.
175- func IsIPv4String (ip string ) bool {
176- return IPFamilyOfString (ip ) == IPv4
177- }
178-
179- // IsIPv4CIDR returns true if cidr is a valid IPv4 CIDR. It returns false if cidr is nil
180- // or an IPv6 CIDR. Its behavior is not defined if cidr is invalid.
181- func IsIPv4CIDR (cidr * net.IPNet ) bool {
182- return IPFamilyOfCIDR (cidr ) == IPv4
183- }
184-
185- // IsIPv4CIDRString returns true if cidr contains a single IPv4 CIDR and nothing else. It
186- // returns false if cidr is an empty string, an IPv6 CIDR, or anything else that is not a
187- // single valid IPv4 CIDR.
188- func IsIPv4CIDRString (cidr string ) bool {
189- return IPFamilyOfCIDRString (cidr ) == IPv4
190- }
0 commit comments