Go/GoLang check IP address in range

前端 未结 5 1997
萌比男神i
萌比男神i 2020-12-29 06:12

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

相关标签:
5条回答
  • 2020-12-29 06:45

    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) <= 0 {
            return true
        }
        return false
    }
    

    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)
        }
    }
    
    0 讨论(0)
  • 2020-12-29 06:45

    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)
    }
    
    0 讨论(0)
  • 2020-12-29 06:48

    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) <= 0 {
            fmt.Printf("%v is between %v and %v\n", trial, ip1, ip2)
            return true
        }
        fmt.Printf("%v is NOT between %v and %v\n", trial, ip1, ip2)
        return false
    }
    
    func main() {
        check("1.2.3.4")
        check("216.14.49.185")
        check("1::16")
    }
    

    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
    
    0 讨论(0)
  • 2020-12-29 06:48

    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

    0 讨论(0)
  • 2020-12-29 07:01

    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 < 27000; i++ {
            IsInRange(inputBytes, lowerBytes, upperBytes)
        }
        endTime := time.Now()
    
        fmt.Println("ELAPSED time port: ", endTime.Sub(startTime))
    
        lower := net.ParseIP("216.14.49.184")
        upper := net.ParseIP("216.14.49.191")
        trial := net.ParseIP("216.14.49.184")
    
        startTime = time.Now()
        for i := 0; i < 27000; i++ {
            IsInRange2(trial, lower, upper)
        }
        endTime = time.Now()
    
        fmt.Println("ELAPSED time bytescompare: ", endTime.Sub(startTime))
    }
    
    func IsInRange2(trial net.IP, lower net.IP, upper net.IP) bool {
        if bytes.Compare(trial, lower) >= 0 && bytes.Compare(trial, upper) <= 0 {
            return true
        }
        return false
    }
    
    func IsInRange(ip []byte, lower []byte, upper []byte) bool {
        //fmt.Printf("given ip len: %d\n", len(ip))
        lowerBoundary := true
        upperBoundary := true
        for i := 0; i < len(lower) && (lowerBoundary || upperBoundary); i++ {
            if lowerBoundary && ip[i] < lower[i] || upperBoundary && ip[i] > 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)
    
    0 讨论(0)
提交回复
热议问题