Skip to content

Commit 59f23ee

Browse files
committed
Add IPFamilyOf(), etc
Often you want to test whether two IPs/CIDRs are of the same family, without actually caring which family they are. While you can do this by checking: if utilsnet.IsIPv6(ip) == utilsnet.IsIPv6CIDR(cidr) { this is somewhat confusing to read and can look like it is checking that they are both IPv6 if you read it quickly. So add new functions so that you can do if utilsnet.IPFamilyOf(ip) == utilsnet.IPFamilyOfCIDR(cidr) {
1 parent 5c9db06 commit 59f23ee

File tree

3 files changed

+73
-34
lines changed

3 files changed

+73
-34
lines changed

net/ipfamily.go

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -106,62 +106,84 @@ func IsDualStackCIDRStrings(cidrs []string) (bool, error) {
106106
return IsDualStackCIDRs(parsedCIDRs)
107107
}
108108

109-
// IsIPv6 returns true if ip is IPv6, or false if it is IPv4, nil, or invalid.
110-
func IsIPv6(ip net.IP) bool {
111-
return ip.To16() != nil && ip.To4() == nil
109+
// IPFamilyOf returns the IP family of ip, or IPFamilyUnknown if it is invalid.
110+
func IPFamilyOf(ip net.IP) IPFamily {
111+
switch {
112+
case ip.To4() != nil:
113+
return IPv4
114+
case ip.To16() != nil:
115+
return IPv6
116+
default:
117+
return IPFamilyUnknown
118+
}
119+
}
120+
121+
// IPFamilyOfString returns the IP family of ip, or IPFamilyUnknown if ip cannot
122+
// be parsed as an IP.
123+
func IPFamilyOfString(ip string) IPFamily {
124+
return IPFamilyOf(ParseIPSloppy(ip))
125+
}
126+
127+
// IPFamilyOfCIDR returns the IP family of cidr.
128+
func IPFamilyOfCIDR(cidr *net.IPNet) IPFamily {
129+
if cidr == nil {
130+
return IPFamilyUnknown
131+
}
132+
return IPFamilyOf(cidr.IP)
133+
}
134+
135+
// IPFamilyOfCIDRString returns the IP family of cidr.
136+
func IPFamilyOfCIDRString(cidr string) IPFamily {
137+
ip, _, _ := ParseCIDRSloppy(cidr)
138+
return IPFamilyOf(ip)
139+
}
140+
141+
// IsIPv6 returns true if netIP is IPv6 (and false if it is IPv4, nil, or invalid).
142+
func IsIPv6(netIP net.IP) bool {
143+
return IPFamilyOf(netIP) == IPv6
112144
}
113145

114146
// IsIPv6String returns true if ip contains a single IPv6 address and nothing else. It
115147
// returns false if ip is an empty string, an IPv4 address, or anything else that is not a
116148
// single IPv6 address.
117149
func IsIPv6String(ip string) bool {
118-
return IsIPv6(ParseIPSloppy(ip))
150+
return IPFamilyOfString(ip) == IPv6
151+
}
152+
153+
// IsIPv6CIDR returns true if a cidr is a valid IPv6 CIDR. It returns false if cidr is
154+
// nil or an IPv4 CIDR. Its behavior is not defined if cidr is invalid.
155+
func IsIPv6CIDR(cidr *net.IPNet) bool {
156+
return IPFamilyOfCIDR(cidr) == IPv6
119157
}
120158

121159
// IsIPv6CIDRString returns true if cidr contains a single IPv6 CIDR and nothing else. It
122160
// returns false if cidr is an empty string, an IPv4 CIDR, or anything else that is not a
123161
// single valid IPv6 CIDR.
124162
func IsIPv6CIDRString(cidr string) bool {
125-
ip, _, _ := ParseCIDRSloppy(cidr)
126-
return IsIPv6(ip)
127-
}
128-
129-
// IsIPv6CIDR returns true if a cidr is a valid IPv6 CIDR. It returns false if cidr is
130-
// nil or an IPv4 CIDR. Its behavior is not defined if cidr is invalid.
131-
func IsIPv6CIDR(cidr *net.IPNet) bool {
132-
if cidr == nil {
133-
return false
134-
}
135-
ip := cidr.IP
136-
return IsIPv6(ip)
163+
return IPFamilyOfCIDRString(cidr) == IPv6
137164
}
138165

139-
// IsIPv4 returns true if ip is IPv4, or false if it is IPv6, nil, or invalid.
140-
func IsIPv4(ip net.IP) bool {
141-
return ip.To4() != nil
166+
// IsIPv4 returns true if netIP is IPv4 (and false if it is IPv6, nil, or invalid).
167+
func IsIPv4(netIP net.IP) bool {
168+
return IPFamilyOf(netIP) == IPv4
142169
}
143170

144171
// IsIPv4String returns true if ip contains a single IPv4 address and nothing else. It
145172
// returns false if ip is an empty string, an IPv6 address, or anything else that is not a
146173
// single IPv4 address.
147174
func IsIPv4String(ip string) bool {
148-
return IsIPv4(ParseIPSloppy(ip))
175+
return IPFamilyOfString(ip) == IPv4
149176
}
150177

151178
// IsIPv4CIDR returns true if cidr is a valid IPv4 CIDR. It returns false if cidr is nil
152179
// or an IPv6 CIDR. Its behavior is not defined if cidr is invalid.
153180
func IsIPv4CIDR(cidr *net.IPNet) bool {
154-
if cidr == nil {
155-
return false
156-
}
157-
ip := cidr.IP
158-
return IsIPv4(ip)
181+
return IPFamilyOfCIDR(cidr) == IPv4
159182
}
160183

161184
// IsIPv4CIDRString returns true if cidr contains a single IPv4 CIDR and nothing else. It
162185
// returns false if cidr is an empty string, an IPv6 CIDR, or anything else that is not a
163186
// single valid IPv4 CIDR.
164187
func IsIPv4CIDRString(cidr string) bool {
165-
ip, _, _ := ParseCIDRSloppy(cidr)
166-
return IsIPv4(ip)
188+
return IPFamilyOfCIDRString(cidr) == IPv4
167189
}

net/ipfamily_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ func TestDualStackCIDRs(t *testing.T) {
228228
}
229229
}
230230

231-
func TestIsIPvXString(t *testing.T) {
231+
func TestIPFamilyOfString(t *testing.T) {
232232
testCases := []struct {
233233
desc string
234234
ip string
@@ -352,9 +352,13 @@ func TestIsIPvXString(t *testing.T) {
352352
}
353353
for _, tc := range testCases {
354354
t.Run(tc.desc, func(t *testing.T) {
355+
family := IPFamilyOfString(tc.ip)
355356
isIPv4 := IsIPv4String(tc.ip)
356357
isIPv6 := IsIPv6String(tc.ip)
357358

359+
if family != tc.family {
360+
t.Errorf("Expect %q family %q, got %q", tc.ip, tc.family, family)
361+
}
358362
if isIPv4 != (tc.family == IPv4) {
359363
t.Errorf("Expect %q ipv4 %v, got %v", tc.ip, tc.family == IPv4, isIPv6)
360364
}
@@ -392,7 +396,7 @@ func mustParseCIDRMask(cidrstr string) net.IPMask {
392396
return cidr.Mask
393397
}
394398

395-
func TestIsIPvX(t *testing.T) {
399+
func TestIsIPFamilyOf(t *testing.T) {
396400
testCases := []struct {
397401
desc string
398402
ip net.IP
@@ -506,9 +510,13 @@ func TestIsIPvX(t *testing.T) {
506510
}
507511
for _, tc := range testCases {
508512
t.Run(tc.desc, func(t *testing.T) {
513+
family := IPFamilyOf(tc.ip)
509514
isIPv4 := IsIPv4(tc.ip)
510515
isIPv6 := IsIPv6(tc.ip)
511516

517+
if family != tc.family {
518+
t.Errorf("Expect family %q, got %q", tc.family, family)
519+
}
512520
if isIPv4 != (tc.family == IPv4) {
513521
t.Errorf("Expect ipv4 %v, got %v", tc.family == IPv4, isIPv6)
514522
}
@@ -519,7 +527,7 @@ func TestIsIPvX(t *testing.T) {
519527
}
520528
}
521529

522-
func TestIsIPvXCIDR(t *testing.T) {
530+
func TestIPFamilyOfCIDR(t *testing.T) {
523531
testCases := []struct {
524532
desc string
525533
cidr string
@@ -619,9 +627,13 @@ func TestIsIPvXCIDR(t *testing.T) {
619627

620628
for _, tc := range testCases {
621629
t.Run(tc.desc, func(t *testing.T) {
630+
family := IPFamilyOfCIDRString(tc.cidr)
622631
isIPv4 := IsIPv4CIDRString(tc.cidr)
623632
isIPv6 := IsIPv6CIDRString(tc.cidr)
624633

634+
if family != tc.family {
635+
t.Errorf("Expect family %v, got %v", tc.family, family)
636+
}
625637
if isIPv4 != (tc.family == IPv4) {
626638
t.Errorf("Expect %q ipv4 %v, got %v", tc.cidr, tc.family == IPv4, isIPv6)
627639
}
@@ -630,8 +642,12 @@ func TestIsIPvXCIDR(t *testing.T) {
630642
}
631643

632644
_, parsed, _ := ParseCIDRSloppy(tc.cidr)
645+
familyParsed := IPFamilyOfCIDR(parsed)
633646
isIPv4Parsed := IsIPv4CIDR(parsed)
634647
isIPv6Parsed := IsIPv6CIDR(parsed)
648+
if familyParsed != family {
649+
t.Errorf("%q gives different results for IPFamilyOfCIDR (%v) and IPFamilyOfCIDRString (%v)", tc.cidr, familyParsed, family)
650+
}
635651
if isIPv4Parsed != isIPv4 {
636652
t.Errorf("%q gives different results for IsIPv4CIDR (%v) and IsIPv4CIDRString (%v)", tc.cidr, isIPv4Parsed, isIPv4)
637653
}

net/port.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,18 @@ func NewLocalPort(desc, ip string, ipFamily IPFamily, port int, protocol Protoco
5858
if protocol != TCP && protocol != UDP {
5959
return nil, fmt.Errorf("Unsupported protocol %s", protocol)
6060
}
61-
if ipFamily != "" && ipFamily != "4" && ipFamily != "6" {
61+
if ipFamily != IPFamilyUnknown && ipFamily != IPv4 && ipFamily != IPv6 {
6262
return nil, fmt.Errorf("Invalid IP family %s", ipFamily)
6363
}
6464
if ip != "" {
6565
parsedIP := ParseIPSloppy(ip)
6666
if parsedIP == nil {
6767
return nil, fmt.Errorf("invalid ip address %s", ip)
6868
}
69-
asIPv4 := parsedIP.To4()
70-
if asIPv4 == nil && ipFamily == IPv4 || asIPv4 != nil && ipFamily == IPv6 {
71-
return nil, fmt.Errorf("ip address and family mismatch %s, %s", ip, ipFamily)
69+
if ipFamily != IPFamilyUnknown {
70+
if IPFamily(parsedIP) != ipFamily {
71+
return nil, fmt.Errorf("ip address and family mismatch %s, %s", ip, ipFamily)
72+
}
7273
}
7374
}
7475
return &LocalPort{Description: desc, IP: ip, IPFamily: ipFamily, Port: port, Protocol: protocol}, nil

0 commit comments

Comments
 (0)