golang区分IPv4 IPv6

时间:2014-03-30 22:58:20

标签: go ip

对于我正在处理的程序,我必须检查IP(将我连接到Internet的IP)是公共还是私有。为此,我需要区分IP是IPv4还是IPv6。

我想根据IP的长度来检查它:

conn, err := net.Dial("udp", "8.9.10.11:2342")
if err != nil {
    fmt.Println("Error", err)
}

localaddr := conn.LocalAddr()

addr, _ := net.ResolveUDPAddr("udp", localaddr.String())

ip := addr.IP

fmt.Println(ip)
fmt.Println(len(ip))

嗯,我的IP是192.168.2.100,所以IPv4,但len(ip)告诉我长度是16,这将是IPv6。 我的错是什么?是否存在区分IPv4和IPv6的其他方法?

5 个答案:

答案 0 :(得分:31)

jtt的答案是正确的,但相当复杂。我只想查看ip.To4() != nil。由于文档说"如果ip不是IPv4地址,To4返回nil"当且仅当地址是IPv4地址时,此条件应返回true

答案 1 :(得分:10)

IP的长度几乎总是16,因为net.IP的内部表示对于任何一种情况都是相同的。来自the documentation

  

请注意,在本文档中,将IP地址称为IPv4地址或IPv6地址是地址的语义属性,而不仅仅是字节片的长度:16字节片仍然可以是IPv4地址

分离这两种类型取决于IP的初始化方式。查看net.IPv4()的代码,可以看到它被初始化为16个字节,前12个字节的值设置为v4InV6Prefix

// IPv4 returns the IP address (in 16-byte form) of the
// IPv4 address a.b.c.d.
func IPv4(a, b, c, d byte) IP {
    p := make(IP, IPv6len)
    copy(p, v4InV6Prefix)
    p[12] = a
    p[13] = b
    p[14] = c
    p[15] = d
    return p
}

v4InV6Prefix定义为:

var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}

如果您想要可靠的差异化,请查看net.IP.To4的来源如何处理它:

// To4 converts the IPv4 address ip to a 4-byte representation.
// If ip is not an IPv4 address, To4 returns nil.
func (ip IP) To4() IP {
    if len(ip) == IPv4len {
            return ip
    }
    if len(ip) == IPv6len &&
            isZeros(ip[0:10]) &&
            ip[10] == 0xff &&
            ip[11] == 0xff {
            return ip[12:16]
    }
    return nil
}

isZeros未导出,因此您必须在本地复制该代码。然后,您可以执行与上述相同的操作,以确定您是否具有IPv4或IPv6。

答案 2 :(得分:3)

accepted answer Evanip.To4() != nil)解决了这个问题。然而,有一些评论和其他答案检查表示是否是IPv4或IPv6并且它们并不总是准确的:

注意 :分别列出了几个有效的IPv4和IPv6有效标记列表。每个条目代表第一个基本条目的变体。可以根据需要组合变体,但出于空间原因,除了消除歧义外,没有列出任何变体组合。

有效的IPv4表示法:

  • "192.168.0.1":基本
  • "192.168.0.1:80":带端口信息

有效的IPv6表示法:

  • "::FFFF:C0A8:1":基本
  • "::FFFF:C0A8:0001":前导零
  • "0000:0000:0000:0000:0000:FFFF:C0A8:1":扩展双冒号
  • "::FFFF:C0A8:1%1":带有区域信息
  • "::FFFF:192.168.0.1":IPv4 literal
  • "[::FFFF:C0A8:1]:80":带端口信息
  • "[::FFFF:C0A8:1%1]:80":带有区域和端口信息

所有这些情况(IPv4和IPv6列表)都将被the net package视为IPv4地址。 IPv6列表的IPv4文字变体将被govalidator视为IPv4。

检查它是IPv4还是IPv6地址的最简单方法如下:

import strings

func IsIPv4(address string) bool {
    return strings.Count(address, ":") < 2
}

func IsIPv6(address string) bool {
    return strings.Count(address, ":") >= 2
}

答案 3 :(得分:2)

我会从网络包中使用.To4(),. To16()来查找它是IPv4还是IPv6地址。 检查blog post。查看博客的最后一部分,例如可能对您有所帮助。

答案 4 :(得分:2)

govalidator检查字符串中是否存在+14

: