27

在 Go/GoLang 中,检查 IP 地址是否在特定范围内的最快方法是什么?

例如,给定范围216.14.49.184to 216.14.49.191,我将如何检查给定的输入 IP 地址是否在该范围内?

4

7 回答 7

40

IP 地址[]byte在 go(类型)中表示为 bigendian 切片,IP因此将使用bytes.Compare.

例如(

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")
}

哪个生产

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
于 2013-11-09T22:42:21.003 回答
29

这已经作为名为 net.Contains 的函数存在于“net”包中的 stdlib 中。你不需要重写已经存在的代码!

请参阅此处的文档。

要使用它,您只需解析所需的子网

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)
    }
}

如果上面的代码没有意义,这里是一个golang 播放链接

于 2016-10-16T16:57:17.807 回答
7

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
}

和 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)
    }
}
于 2014-09-15T12:48:53.357 回答
3

我从此处找到的 C# 示例中移植了代码:https ://stackoverflow.com/a/2138724/1655418

由于某种原因,它最终比尼克的解决方案快 1 毫秒。

我的问题是“最快”的方式,所以我想我会发布我的,看看社区的想法。

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
}

我的结果:

=== 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)
于 2013-11-10T00:24:58.283 回答
2

像inet_pton这样的实现怎么样?结果易于存储。

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)
}
于 2013-11-13T05:50:10.817 回答
1

看要点

ipmatcher.go

type IPMatcher struct {
    IP net.IP
    SubNet *net.IPNet
}
type IPMatchers []*IPMatcher

func NewIPMatcher(ipStr string) (*IPMatcher, error) {
    ip, subNet, err := net.ParseCIDR(ipStr)
    if err != nil {
        ip = net.ParseIP(ipStr)
        if ip == nil {
            return nil, errors.New("invalid IP: "+ipStr)
        }
    }
    return &IPMatcher{ip, subNet}, nil
}

func (m IPMatcher) Match(ipStr string) bool {
    ip := net.ParseIP(ipStr)
    if ip == nil {
        return false
    }
    return m.IP.Equal(ip) || m.SubNet != nil && m.SubNet.Contains(ip)
}

func NewIPMatchers(ips []string) (list IPMatchers, err error) {
    for _, ipStr := range ips {
        var m *IPMatcher
        m, err = NewIPMatcher(ipStr)
        if err != nil {
            return
        }
        list = append(list, m)
    }
    return
}

func IPContains(ipMatchers []*IPMatcher, ip string) bool {
    for _, m := range ipMatchers {
        if m.Match(ip) {
            return true
        }
    }
    return false
}

func (ms IPMatchers) Match(ip string) bool {
    return IPContains(ms, ip)
}

ipmatcher_test.go

import "testing"

func TestIPMatcher(t *testing.T)  {
    a, errA := NewIPMatcher("127.0.0.1")
    if errA != nil {
        t.Error(errA)
    }
    if a.IP.String() != "127.0.0.1" || a.SubNet != nil {
        t.Error("ip parse error")
    }

    b, errB := NewIPMatcher("192.168.1.1/24")
    if errB != nil {
        t.Error(errB)
    }
    if b.IP.String() != "192.168.1.1" || b.SubNet == nil {
        t.Errorf("ip match error: %s, %v", b.IP.String(), b.SubNet)
    }
    if !b.Match("192.168.1.1") || !b.Match("192.168.1.2") {
        t.Error("ip match error")
    }
}

func TestIPMatchers(t *testing.T)  {
    var WhiteListIPs = []string{"127.0.0.1", "192.168.1.0/24", "10.1.0.0/16"}
    M, err := NewIPMatchers(WhiteListIPs)
    if err != nil {
        t.Error(err)
    }
    if !M.Match("127.0.0.1") || !M.Match("192.168.1.1") || !M.Match("192.168.1.199") ||
        !M.Match("10.1.0.1") || !M.Match("10.1.3.1") {
        t.Error("ip match error")
    }
    if M.Match("127.0.0.2") || M.Match("192.168.2.1") || M.Match("10.2.0.1") {
        t.Error("ip match error 2")
    }
}

看要点

于 2021-07-08T09:18:04.797 回答
0

IPAddress Go 库可以使用IPv4 和 IPv6 地址以多态方式快速完成此操作。 存储库在这里。免责声明:我是项目经理。

isInRange("216.14.49.184", "216.14.49.191", "216.14.49.190")
isInRange("2001:0db8:85a3::8a2e:0370:7334", "2001:0db8:85a3::8a00:ff:ffff",
    "2001:0db8:85a3::8a03:a:b")

func getAddress(str string) *ipaddr.IPAddress {
    return ipaddr.NewIPAddressString(str).GetAddress()
}

func isInRange(range1Str, range2Str, addrStr string) {
    range1, range2, addr :=
        getAddress(range1Str), getAddress(range2Str), getAddress(addrStr)
    rng := range1.SpanWithRange(range2)
    result := rng.Contains(addr)
    fmt.Printf("%v contains %v %t\n", rng, addr, result)
    return
}

输出:

216.14.49.184 -> 216.14.49.191 contains 216.14.49.190 true
2001:db8:85a3::8a00:ff:ffff -> 2001:db8:85a3::8a2e:370:7334 contains 2001:db8:85a3::8a03:a:b true
于 2022-01-15T02:32:23.533 回答