Go - 检查 IP 地址是否在网络中

Go - check if IP address is in a network

鉴于:

如何判断B是否在A中?

所有地址都是以下形式的字符串变量:[IP address in dot-decimal notation]/[subnet mask]。我是否应该尝试通过操纵字符串来做到这一点(初步想法)。有不同的路径吗?

这是 Python 的相同问题:

以及 Go 的另一种方法:

2022 年 3 月更新
对于 Go 1.18,检查 by blackgreen

Go net package包括以下功能:

  • ParseCIDR: 取一个代表 IP/mask 和 returns 的字符串 IP 和 IPNet
  • IPNet.Contains:检查IP是否在 网络

这应该能满足您的需求。

2022 年 3 月更新
对于 Go 1.18,检查 by 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")
    }

}

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

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

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
}

The ipaddress-go Go library 以多态方式同时支持 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

Go 1.18

您可以使用新包 net/netip。该包定义了类型netip.Addr,这是对旧类型的重大改进:

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

您可以使用方法 Prefix.Contains:

检查 IP 是否在网络内

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.

一个例子:

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 团队成员)的 this blog postnet/netip 包基于该工作。