检查IP地址是否在网络中的Go语言实现

16

给定:

  • 一个网络地址A:(172.17.0.0/16)
  • 来自主机B的IP地址:(172.17.0.2/16)

如何判断B是否在A中?

所有地址都是以下形式的字符串变量:[点十进制表示法中的IP地址]/[子网掩码]。我应该通过操作字符串来尝试吗(初步想法)?还有其他不同的方法吗?

下面是Python的相同问题:

以及Go的另一种方法:

2022年3月更新
对于Go 1.18,请查看下面的答案,由blackgreen提供。

6个回答

22

Go net包 包含以下函数:

  • ParseCIDR:接受一个表示IP/mask的字符串并返回一个IP和IPNet对象
  • IPNet.Contains:检查一个IP地址是否在网络中

这些函数应该能够满足您的需求。


11

2022年3月更新:
如需适用于Go 1.18的内容,请查看下面的答案,作者是blackgreen

根据Zoyd的反馈...

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

}

10

Go 1.18

你可以使用新的 net/netip 包。该包定义了类型 netip.Addr,相对于旧版本有明显的改进:

与 net.IP 类型相比,该包的 Addr 类型占用更少的内存,是不可变的,并且支持比较运算符(==),可以作为 map 的键。

你可以使用方法 Prefix.Contains 检查 IP 是否位于网络中:

Contains 方法会判断网络 p 是否包含 IP。

IPv4 地址不会匹配 IPv6 前缀。v6 映射的 IPv6 地址不会匹配 IPv4 前缀。零值 IP 不会匹配任何前缀。如果 IP 具有 IPv6 区域标识,则 Contains 返回 false,因为前缀将删除区域标识。

下面是一个例子:

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
}

如果您想检查的IP地址也有子网掩码,比如在您的示例中使用 172.17.0.2/16 ,您可以再次使用 ip, err := netip.ParsePrefix,然后使用 ip.Addr() 获取地址并将其传递给Contains
代码演示: https://go.dev/play/p/ikWRpPa1egI
对于那些对实现细节感兴趣的人,您可以看看 Brad Fitzpatrick(Go团队的前成员)的这篇博客文章net / netip包是基于该工作完成的。

你的方法 netip.ParsePrefix("172.17.0.0/16") 比之前的快:基准测试Fib40-8 2038528次操作,每次722.7纳秒,160字节内存分配,8次内存分配相比于 net.ParseCIDR 基准测试Fib40-8 1107955次操作,每次1092纳秒,288字节内存分配,17次内存分配。 - Adrien Parrochia

9

根据tgogos的回答:

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

1

ipaddress-go Go库 支持IPv4和IPv6的多态方式,并支持子网,包括检查地址或子网是否包含在包含子网中的方法。它还允许使用不仅仅是CIDR子网。免责声明:我是该库的项目经理。

示例代码:

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

输出:

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

0

基于上面的答案,这样人们就可以轻松地将代码复制并粘贴到他们的项目中。

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
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接