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

huangapple go评论121阅读模式
英文:

Go - check if IP address is in a network

问题

给定:

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

我们如何判断 B 是否在 A 中?

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

以下是关于 Python 的同样问题的链接:

以及使用 Go 的另一种方法:

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

英文:

Given:

  • a network address A: (172.17.0.0/16)
  • and an IP address from a host B: (172.17.0.2/16)

how can we say if B is in A?

All addresses are string variables in the following form: [IP address in dot-decimal notation]/[subnet mask]. Should I try to do it by manipulating strings (initial thoughts). Is there a different path?

Here is the same question for Python:

and another approach with Go:

UPDATE March 2022
👉 for Go 1.18, check the answer below by blackgreen

答案1

得分: 21

Go net包 包含以下函数:

  • ParseCIDR:接受表示IP/掩码的字符串,并返回一个IP和一个IPNet
  • IPNet.Contains:检查一个IP是否在网络中

这应该满足你的需求。

英文:

The Go net package includes the following functions:

  • ParseCIDR: takes a string representing an IP/mask and returns an
    IP and an IPNet
  • IPNet.Contains: checks whether an IP is in a
    network

This should cover your needs.

答案2

得分: 11

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

根据Zoyd的反馈...

https://play.golang.org/p/wdv2sPetmt

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. )
  6. func main() {
  7. A := "172.17.0.0/16"
  8. B := "172.17.0.2/16"
  9. ipA,ipnetA,_ := net.ParseCIDR(A)
  10. ipB,ipnetB,_ := net.ParseCIDR(B)
  11. fmt.Println("网络地址 A: ", A)
  12. fmt.Println("IP地址 B: ", B)
  13. fmt.Println("ipA : ", ipA)
  14. fmt.Println("ipnetA : ", ipnetA)
  15. fmt.Println("ipB : ", ipB)
  16. fmt.Println("ipnetB : ", ipnetB)
  17. fmt.Printf("\nA (%s) 是否包含 B (%s)?\n", ipnetA, ipB)
  18. if ipnetA.Contains(ipB) {
  19. fmt.Println("是")
  20. } else {
  21. fmt.Println("否")
  22. }
  23. }
英文:

UPDATE March 2022
👉 for Go 1.18, check the answer below by blackgreen

Based on Zoyd's feedback...

https://play.golang.org/p/wdv2sPetmt

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. )
  6. func main() {
  7. A := "172.17.0.0/16"
  8. B := "172.17.0.2/16"
  9. ipA,ipnetA,_ := net.ParseCIDR(A)
  10. ipB,ipnetB,_ := net.ParseCIDR(B)
  11. fmt.Println("Network address A: ", A)
  12. fmt.Println("IP address B: ", B)
  13. fmt.Println("ipA : ", ipA)
  14. fmt.Println("ipnetA : ", ipnetA)
  15. fmt.Println("ipB : ", ipB)
  16. fmt.Println("ipnetB : ", ipnetB)
  17. fmt.Printf("\nDoes A (%s) contain: B (%s)?\n", ipnetA, ipB)
  18. if ipnetA.Contains(ipB) {
  19. fmt.Println("yes")
  20. } else {
  21. fmt.Println("no")
  22. }
  23. }

答案3

得分: 10

Go 1.18

您可以使用新的net/netip包。该包定义了netip.Addr类型,相比旧类型有显著改进:

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

您可以使用Prefix.Contains方法来检查一个IP是否在网络中:

Contains方法报告网络p是否包含IP。

IPv4地址不会匹配IPv6前缀。v6-mapped IPv6地址不会匹配IPv4前缀。零值IP不会匹配任何前缀。如果IP具有IPv6区域,Contains返回false,因为前缀会去除区域。

示例代码:

  1. package main
  2. import (
  3. "fmt"
  4. "net/netip"
  5. )
  6. func main() {
  7. network, err := netip.ParsePrefix("172.17.0.0/16")
  8. if err != nil {
  9. panic(err)
  10. }
  11. ip, err := netip.ParseAddr("172.17.0.2")
  12. if err != nil {
  13. panic(err)
  14. }
  15. b := network.Contains(ip)
  16. fmt.Println(b) // true
  17. }

如果您要检查的IP还有子网掩码,例如您的示例中的172.17.0.2/16,您可以再次使用ip, err := netip.ParsePrefix,然后使用ip.Addr()获取地址,并将其传递给Contains方法。

在playground中的代码:https://go.dev/play/p/ikWRpPa1egI


对于那些对实现细节感兴趣的人,您可以查看Brad Fitzpatrick(Go团队的前成员)的博文net/netip包基于该工作。

英文:

Go 1.18

You can use the new package net/netip. The package defines the type netip.Addr which is a significant improvement over the old one:

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

You can check if an IP is within a network with the method Prefix.Contains:

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

An example:

  1. package main
  2. import (
  3. "fmt"
  4. "net/netip"
  5. )
  6. func main() {
  7. network, err := netip.ParsePrefix("172.17.0.0/16")
  8. if err != nil {
  9. panic(err)
  10. }
  11. ip, err := netip.ParseAddr("172.17.0.2")
  12. if err != nil {
  13. panic(err)
  14. }
  15. b := network.Contains(ip)
  16. fmt.Println(b) // true
  17. }

If the IP you want to check also has the subnet mask, like 172.17.0.2/16 as in your example, you can use ip, err := netip.ParsePrefix again, and then obtain the address with ip.Addr() and pass that to Contains.

Code in the playground: https://go.dev/play/p/ikWRpPa1egI

<hr>

For those who are interested in the implementation details, you can see this blog post by Brad Fitzpatrick (former member of the Go team). The net/netip package is based on that work.

答案4

得分: 9

根据tgogos的答案:

  1. package main
  2. import (
  3. "fmt"
  4. "net"
  5. )
  6. func main() {
  7. A := "172.17.0.0/16"
  8. B := "172.17.0.2"
  9. _, ipnetA, _ := net.ParseCIDR(A)
  10. ipB := net.ParseIP(B)
  11. fmt.Printf("\nA(%s)是否包含B(%s)?\n", ipnetA, ipB)
  12. if ipnetA.Contains(ipB) {
  13. fmt.Println("是的")
  14. } else {
  15. fmt.Println("不是")
  16. }
  17. }
英文:

Based on tgogos's answer:

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;net&quot;
  5. )
  6. func main() {
  7. A := &quot;172.17.0.0/16&quot;
  8. B := &quot;172.17.0.2&quot;
  9. _, ipnetA, _ := net.ParseCIDR(A)
  10. ipB := net.ParseIP(B)
  11. fmt.Printf(&quot;\nDoes A (%s) contain: B (%s)?\n&quot;, ipnetA, ipB)
  12. if ipnetA.Contains(ipB) {
  13. fmt.Println(&quot;yes&quot;)
  14. } else {
  15. fmt.Println(&quot;no&quot;)
  16. }
  17. }

答案5

得分: 1

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

示例代码:

  1. contains("172.17.0.0/16", "172.17.0.2/16")
  2. contains("10.10.20.0/30", "10.10.20.3")
  3. contains("10.10.20.0/30", "10.10.20.5")
  4. contains("10.10.20.0/30", "10.10.20.0/31")
  5. contains("1::/64", "1::1")
  6. contains("1::/64", "2::1")
  7. contains("1::/64", "1::/32")
  8. contains("1::/64", "1::/112")
  9. contains("1::3-4:5-6", "1::4:5")
  10. contains("1-2::/64", "2::")
  11. contains("bla", "foo")
  12. func contains(network, address string) {
  13. one, two := ipaddr.NewIPAddressString(network),
  14. ipaddr.NewIPAddressString(address)
  15. fmt.Printf("%v contains %v %v\n", one, two, one.Contains(two))
  16. }

输出:

  1. 172.17.0.0/16 contains 172.17.0.2/16 true
  2. 10.10.20.0/30 contains 10.10.20.3 true
  3. 10.10.20.0/30 contains 10.10.20.5 false
  4. 10.10.20.0/30 contains 10.10.20.0/31 true
  5. 1::/64 contains 1::1 true
  6. 1::/64 contains 2::1 false
  7. 1::/64 contains 1::/32 false
  8. 1::/64 contains 1::/112 true
  9. 1::3-4:5-6 contains 1::4:5 true
  10. 1-2::/64 contains 2:: true
  11. bla contains foo false
英文:

The ipaddress-go Go library supports both IPv4 and IPv6 in a polymorphic manner and supports subnets, including methods that check for containment of an address or subnet in a containing subnet. It also allows for more than just CIDR subnets. Disclaimer: I am the project manager of that library.

Example code:

  1. contains(&quot;172.17.0.0/16&quot;, &quot;172.17.0.2/16&quot;)
  2. contains(&quot;10.10.20.0/30&quot;, &quot;10.10.20.3&quot;)
  3. contains(&quot;10.10.20.0/30&quot;, &quot;10.10.20.5&quot;)
  4. contains(&quot;10.10.20.0/30&quot;, &quot;10.10.20.0/31&quot;)
  5. contains(&quot;1::/64&quot;, &quot;1::1&quot;)
  6. contains(&quot;1::/64&quot;, &quot;2::1&quot;)
  7. contains(&quot;1::/64&quot;, &quot;1::/32&quot;)
  8. contains(&quot;1::/64&quot;, &quot;1::/112&quot;)
  9. contains(&quot;1::3-4:5-6&quot;, &quot;1::4:5&quot;)
  10. contains(&quot;1-2::/64&quot;, &quot;2::&quot;)
  11. contains(&quot;bla&quot;, &quot;foo&quot;)
  12. func contains(network, address string) {
  13. one, two := ipaddr.NewIPAddressString(network),
  14. ipaddr.NewIPAddressString(address)
  15. fmt.Printf(&quot;%v contains %v %v\n&quot;, one, two, one.Contains(two))
  16. }

Output:

  1. 172.17.0.0/16 contains 172.17.0.2/16 true
  2. 10.10.20.0/30 contains 10.10.20.3 true
  3. 10.10.20.0/30 contains 10.10.20.5 false
  4. 10.10.20.0/30 contains 10.10.20.0/31 true
  5. 1::/64 contains 1::1 true
  6. 1::/64 contains 2::1 false
  7. 1::/64 contains 1::/32 false
  8. 1::/64 contains 1::/112 true
  9. 1::3-4:5-6 contains 1::4:5 true
  10. 1-2::/64 contains 2:: true
  11. bla contains foo false

答案6

得分: 0

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

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net"
  6. )
  7. func main() {
  8. // True
  9. firstCheck, err := cidrRangeContains("10.0.0.0/24", "10.0.0.1")
  10. if err != nil {
  11. log.Println(err)
  12. }
  13. fmt.Println(firstCheck)
  14. // False
  15. secondCheck, err := cidrRangeContains("10.0.0.0/24", "127.0.0.1")
  16. if err != nil {
  17. log.Println(err)
  18. }
  19. fmt.Println(secondCheck)
  20. }
  21. // 检查某个IP是否在CIDR范围内。
  22. func cidrRangeContains(cidrRange string, checkIP string) (bool, error) {
  23. _, ipnet, err := net.ParseCIDR(cidrRange)
  24. if err != nil {
  25. return false, err
  26. }
  27. secondIP := net.ParseIP(checkIP)
  28. return ipnet.Contains(secondIP), err
  29. }
英文:

Based on the answer above so people can easily copy and paste the code into their projects.

  1. package main
  2. import (
  3. &quot;fmt&quot;
  4. &quot;log&quot;
  5. &quot;net&quot;
  6. )
  7. func main() {
  8. // True
  9. firstCheck, err := cidrRangeContains(&quot;10.0.0.0/24&quot;, &quot;10.0.0.1&quot;)
  10. if err != nil {
  11. log.Println(err)
  12. }
  13. fmt.Println(firstCheck)
  14. // False
  15. secondCheck, err := cidrRangeContains(&quot;10.0.0.0/24&quot;, &quot;127.0.0.1&quot;)
  16. if err != nil {
  17. log.Println(err)
  18. }
  19. fmt.Println(secondCheck)
  20. }
  21. // Check if a certain ip in a cidr range.
  22. func cidrRangeContains(cidrRange string, checkIP string) (bool, error) {
  23. _, ipnet, err := net.ParseCIDR(cidrRange)
  24. if err != nil {
  25. return false, err
  26. }
  27. secondIP := net.ParseIP(checkIP)
  28. return ipnet.Contains(secondIP), err
  29. }

huangapple
  • 本文由 发表于 2017年4月7日 17:30:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/43274579.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定