18

Given:

  • a network address A: (172.17.0.0/16)
  • and an IP address from a host B: (172.17.0.2/16)

how can we say if B is in A?

All addresses are string variables in the following form: [IP address in dot-decimal notation]/[subnet mask]. Should I try to do it by manipulating strings (initial thoughts). Is there a different path?

Here is the same question for Python:

and another approach with Go:

UPDATE March 2022
👉 for Go 1.18, check the answer below by blackgreen

6 Answers 6

23

The Go net package includes the following functions:

  • ParseCIDR: takes a string representing an IP/mask and returns an IP and an IPNet
  • IPNet.Contains: checks whether an IP is in a network

This should cover your needs.

Sign up to request clarification or add additional context in comments.

Comments

16

Go 1.18

You can use the new package net/netip. The package defines the type netip.Addr which is a significant improvement over the old one:

Compared to the net.IP type, this package's Addr type takes less memory, is immutable, and is comparable (supports == and being a map key).

You can check if an IP is within a network with the method Prefix.Contains:

Contains reports whether the network p includes ip.

An IPv4 address will not match an IPv6 prefix. A v6-mapped IPv6 address will not match an IPv4 prefix. A zero-value IP will not match any prefix. If ip has an IPv6 zone, Contains returns false, because Prefixes strip zones.

An example:

package main import ( "fmt" "net/netip" ) func main() { network, err := netip.ParsePrefix("172.17.0.0/16") if err != nil { panic(err) } ip, err := netip.ParseAddr("172.17.0.2") if err != nil { panic(err) } b := network.Contains(ip) fmt.Println(b) // true } 

If the IP you want to check also has the subnet mask, like 172.17.0.2/16 as in your example, you can use ip, err := netip.ParsePrefix again, and then obtain the address with ip.Addr() and pass that to Contains.

Code in the playground: https://go.dev/play/p/ikWRpPa1egI


For those who are interested in the implementation details, you can see this blog post by Brad Fitzpatrick (former member of the Go team). The net/netip package is based on that work.

1 Comment

Your methods netip.ParsePrefix("172.17.0.0/16") is faster than previous : BenchmarkFib40-8 2038528 722.7 ns/op 160 B/op 8 allocs/op against net.ParseCIDR BenchmarkFib40-8 1107955 1092 ns/op 288 B/op 17 allocs/op
12

UPDATE March 2022
👉 for Go 1.18, check the answer below by blackgreen

Based on Zoyd's feedback...

https://play.golang.org/p/wdv2sPetmt

package main import ( "fmt" "net" ) func main() { A := "172.17.0.0/16" B := "172.17.0.2/16" ipA,ipnetA,_ := net.ParseCIDR(A) ipB,ipnetB,_ := net.ParseCIDR(B) fmt.Println("Network address A: ", A) fmt.Println("IP address B: ", B) fmt.Println("ipA : ", ipA) fmt.Println("ipnetA : ", ipnetA) fmt.Println("ipB : ", ipB) fmt.Println("ipnetB : ", ipnetB) fmt.Printf("\nDoes A (%s) contain: B (%s)?\n", ipnetA, ipB) if ipnetA.Contains(ipB) { fmt.Println("yes") } else { fmt.Println("no") } } 

Comments

9

Based on tgogos's answer:

package main import ( "fmt" "net" ) func main() { A := "172.17.0.0/16" B := "172.17.0.2" _, ipnetA, _ := net.ParseCIDR(A) ipB := net.ParseIP(B) fmt.Printf("\nDoes A (%s) contain: B (%s)?\n", ipnetA, ipB) if ipnetA.Contains(ipB) { fmt.Println("yes") } else { fmt.Println("no") } } 

Comments

1

The ipaddress-go Go library supports both IPv4 and IPv6 in a polymorphic manner and supports subnets, including methods that check for containment of an address or subnet in a containing subnet. It also allows for more than just CIDR subnets. Disclaimer: I am the project manager of that library.

Example code:

contains("172.17.0.0/16", "172.17.0.2/16") contains("10.10.20.0/30", "10.10.20.3") contains("10.10.20.0/30", "10.10.20.5") contains("10.10.20.0/30", "10.10.20.0/31") contains("1::/64", "1::1") contains("1::/64", "2::1") contains("1::/64", "1::/32") contains("1::/64", "1::/112") contains("1::3-4:5-6", "1::4:5") contains("1-2::/64", "2::") contains("bla", "foo") func contains(network, address string) { one, two := ipaddr.NewIPAddressString(network), ipaddr.NewIPAddressString(address) fmt.Printf("%v contains %v %v\n", one, two, one.Contains(two)) } 

Output:

172.17.0.0/16 contains 172.17.0.2/16 true 10.10.20.0/30 contains 10.10.20.3 true 10.10.20.0/30 contains 10.10.20.5 false 10.10.20.0/30 contains 10.10.20.0/31 true 1::/64 contains 1::1 true 1::/64 contains 2::1 false 1::/64 contains 1::/32 false 1::/64 contains 1::/112 true 1::3-4:5-6 contains 1::4:5 true 1-2::/64 contains 2:: true bla contains foo false 

Comments

0

Based on the answer above so people can easily copy and paste the code into their projects.

package main import ( "fmt" "log" "net" ) func main() { // True firstCheck, err := cidrRangeContains("10.0.0.0/24", "10.0.0.1") if err != nil { log.Println(err) } fmt.Println(firstCheck) // False secondCheck, err := cidrRangeContains("10.0.0.0/24", "127.0.0.1") if err != nil { log.Println(err) } fmt.Println(secondCheck) } // Check if a certain ip in a cidr range. func cidrRangeContains(cidrRange string, checkIP string) (bool, error) { _, ipnet, err := net.ParseCIDR(cidrRange) if err != nil { return false, err } secondIP := net.ParseIP(checkIP) return ipnet.Contains(secondIP), err } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.