In Go/GoLang, what is the fastest way to check if an IP address is in a specific range?
For example, given range 216.14.49.184 to 216.14.49.191, how would I check if a given input IP address is in that range?
In Go/GoLang, what is the fastest way to check if an IP address is in a specific range?
For example, given range 216.14.49.184 to 216.14.49.191, how would I check if a given input IP address is in that range?
IP addresses are represented as bigendian []byte slices in go (the IP type) so will compare correctly using bytes.Compare.
Eg (play)
package main import ( "bytes" "fmt" "net" ) var ( ip1 = net.ParseIP("216.14.49.184") ip2 = net.ParseIP("216.14.49.191") ) func check(ip string) bool { trial := net.ParseIP(ip) if trial.To4() == nil { fmt.Printf("%v is not an IPv4 address\n", trial) return false } if bytes.Compare(trial, ip1) >= 0 && bytes.Compare(trial, ip2) Which produces
1.2.3.4 is NOT between 216.14.49.184 and 216.14.49.191 216.14.49.185 is between 216.14.49.184 and 216.14.49.191 1::16 is not an IPv4 address The generic version for ipv4/ipv6.
ip.go:
package ip import ( "bytes" "net" "github.com/golang/glog" ) //test to determine if a given ip is between two others (inclusive) func IpBetween(from net.IP, to net.IP, test net.IP) bool { if from == nil || to == nil || test == nil { glog.Warning("An ip input is nil") // or return an error!? return false } from16 := from.To16() to16 := to.To16() test16 := test.To16() if from16 == nil || to16 == nil || test16 == nil { glog.Warning("An ip did not convert to a 16 byte") // or return an error!? return false } if bytes.Compare(test16, from16) >= 0 && bytes.Compare(test16, to16) and ip_test.go:
package ip import ( "net" "testing" ) func TestIPBetween(t *testing.T) { HandleIpBetween(t, "0.0.0.0", "255.255.255.255", "128.128.128.128", true) HandleIpBetween(t, "0.0.0.0", "128.128.128.128", "255.255.255.255", false) HandleIpBetween(t, "74.50.153.0", "74.50.153.4", "74.50.153.0", true) HandleIpBetween(t, "74.50.153.0", "74.50.153.4", "74.50.153.4", true) HandleIpBetween(t, "74.50.153.0", "74.50.153.4", "74.50.153.5", false) HandleIpBetween(t, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "74.50.153.4", "74.50.153.2", false) HandleIpBetween(t, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:8334", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", true) HandleIpBetween(t, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:8334", "2001:0db8:85a3:0000:0000:8a2e:0370:7350", true) HandleIpBetween(t, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:8334", "2001:0db8:85a3:0000:0000:8a2e:0370:8334", true) HandleIpBetween(t, "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "2001:0db8:85a3:0000:0000:8a2e:0370:8334", "2001:0db8:85a3:0000:0000:8a2e:0370:8335", false) HandleIpBetween(t, "::ffff:192.0.2.128", "::ffff:192.0.2.250", "::ffff:192.0.2.127", false) HandleIpBetween(t, "::ffff:192.0.2.128", "::ffff:192.0.2.250", "::ffff:192.0.2.128", true) HandleIpBetween(t, "::ffff:192.0.2.128", "::ffff:192.0.2.250", "::ffff:192.0.2.129", true) HandleIpBetween(t, "::ffff:192.0.2.128", "::ffff:192.0.2.250", "::ffff:192.0.2.250", true) HandleIpBetween(t, "::ffff:192.0.2.128", "::ffff:192.0.2.250", "::ffff:192.0.2.251", false) HandleIpBetween(t, "::ffff:192.0.2.128", "::ffff:192.0.2.250", "192.0.2.130", true) HandleIpBetween(t, "192.0.2.128", "192.0.2.250", "::ffff:192.0.2.130", true) HandleIpBetween(t, "idonotparse", "192.0.2.250", "::ffff:192.0.2.130", false) } func HandleIpBetween(t *testing.T, from string, to string, test string, assert bool) { res := IpBetween(net.ParseIP(from), net.ParseIP(to), net.ParseIP(test)) if res != assert { t.Errorf("Assertion (have: %s should be: %s) failed on range %s-%s with test %s", res, assert, from, to, test) } } I ported over the code from a C# example found here: https://stackoverflow.com/a/2138724/1655418
And for some reason it ends up being 1ms faster than Nick's solution.
My question was for the "fastest" way, so I figured I'd post mine and see what the community thinks.
package iptesting import ( "fmt" "testing" "net" "time" "bytes" ) func TestIPRangeTime(t *testing.T) { lowerBytes := net.ParseIP("216.14.49.184").To4() upperBytes := net.ParseIP("216.14.49.191").To4() inputBytes := net.ParseIP("216.14.49.184").To4() startTime := time.Now() for i := 0; i = 0 && bytes.Compare(trial, upper) upper[i] { return false } if ip[i] == lower[i] { if lowerBoundary { lowerBoundary = true } else { lowerBoundary = false } //lowerBoundary &= true } else { lowerBoundary = false //lowerBoundary &= false } if ip[i] == upper[i] { //fmt.Printf("matched upper\n") if upperBoundary { upperBoundary = true } else { upperBoundary = false } //upperBoundary &= true } else { upperBoundary = false //upperBoundary &= false } } return true } My results:
=== RUN TestIPRangeTime ELAPSED time port: 1.0001ms ELAPSED time bytescompare: 2.0001ms --- PASS: TestIPRangeTime (0.00 seconds) === RUN TestIPRangeTime ELAPSED time port: 1ms ELAPSED time bytescompare: 2.0002ms --- PASS: TestIPRangeTime (0.00 seconds) === RUN TestIPRangeTime ELAPSED time port: 1.0001ms ELAPSED time bytescompare: 2.0001ms --- PASS: TestIPRangeTime (0.00 seconds) === RUN TestIPRangeTime ELAPSED time port: 1.0001ms ELAPSED time bytescompare: 2.0001ms --- PASS: TestIPRangeTime (0.00 seconds) How about some implementation like inet_pton? The result is easy to be stored.
func IP2Integer(ip *net.IP) (int64, error) { ip4 := ip.To4() if ip4 == nil { return 0, fmt.Errorf("illegal: %v", ip) } bin := make([]string, len(ip4)) for i, v := range ip4 { bin[i] = fmt.Sprintf("%08b", v) } return strconv.ParseInt(strings.Join(bin, ""), 2, 64) } This is already in the stdlib in the "net" package as a function called net.Contains. You dont need to rewrite code that already exists!
See documentation here.
To use it you just have to parse the desired subnets
network := "192.168.5.0/24" clientips := []string{ "192.168.5.1", "192.168.6.0", } _, subnet, _ := net.ParseCIDR(network) for _, clientip := range clientips { ip := net.ParseIP(clientip) if subnet.Contains(ip) { fmt.Println("IP in subnet", clientip) } } In case the above code doesn't make sense here is a google play link