IPv6 中的 ULA 地址并不是 Global Routable 地址

陪她去流浪 桃子 2023年12月07日 阅读次数:370

今天在外的时候,突然发现手机“回不了家”了,以为是 DDNS 域名解析滞后,但是过了一阵还是不行。 于是手动域名解析了一下,发现前缀是:FDD7:...,咋一看,这么大,肯定不是公网地址(Global Routable)。平常见到中国电信的地址都是 240e: 开头的。 但是我的代码里面明明已经通过 IsGlobalUnicast() 函数来判断仅全局单播地址才会更新 DDNS 的,出 BUG 了?🤔

回到家后,我用 ifconfig 看了一眼虚拟机网卡的地址,震惊🤯我妈:

proxy:~$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 52:54:00:XX:XX:XX
          inet6 addr: fdd7:ca06:.../64 Scope:Global
          inet6 addr: 240e:3b7:.../64 Scope:Global
          inet6 addr: fe80::5054:ff:fe5b:5adb/64 Scope:Link

Scope:Link 可以理解,本地链路地址,代码里面忽略了。但是,FDD7: 被标识为 Scope:Global 是什么鬼啊? 我以为是虚拟机系统显示问题,然后看了一眼 MacOS 的,同样如此。进入知识盲区了。以前是没有见过这种地址的,不知道什么情况下生成?🤔

于是搜索了一下,这个 FDD7: 地址(属于 CIDR 范围 FC00::/7)叫作:Unique local address(中文应该叫:唯一本地地址)。 维基百科说它类似 IPv4 私有地址,并且不是全局可路由的:

A unique local address (ULA)[1] is an Internet Protocol version 6 (IPv6) address in the address range fc00::/7.[2] These addresses are non-globally reachable[3] (routable only within the scope of private networks, but not the global IPv6 Internet). For this reason, ULAs are somewhat analogous to IPv4 private network addressing, but with significant differences. Unique local addresses may be used freely, without centralized registration, inside a single site or organization or spanning a limited number of sites or organizations.

所以 Go 为什么认为它是 Global Unicast?🤔 然后我就跑去看了一下它的源代码

// IsGlobalUnicast reports whether ip is a global unicast
// address.
//
// The identification of global unicast addresses uses address type
// identification as defined in RFC 1122, RFC 4632 and RFC 4291 with
// the exception of IPv4 directed broadcast addresses.
// It returns true even if ip is in IPv4 private address space or
// local IPv6 unicast address space.
func (ip IP) IsGlobalUnicast() bool {
	return (len(ip) == IPv4len || len(ip) == IPv6len) &&
		!ip.Equal(IPv4bcast) &&
		!ip.IsUnspecified() &&
		!ip.IsLoopback() &&
		!ip.IsMulticast() &&
		!ip.IsLinkLocalUnicast()
}

我以为它排除了这么多终能得到我想要的地址了,事与愿违。 看到文档的最后一句 It returns true even if ip is in IPv4 private address space or local IPv6 unicast address space., 这...确实是符合此函数声明的预期的行为。然后它还有一个 IsPrivate() 函数:

// IsPrivate reports whether ip is a private address, according to
// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses).
func (ip IP) IsPrivate() bool {
	if ip4 := ip.To4(); ip4 != nil {
		// Following RFC 1918, Section 3. Private Address Space which says:
		//   The Internet Assigned Numbers Authority (IANA) has reserved the
		//   following three blocks of the IP address space for private internets:
		//     10.0.0.0        -   10.255.255.255  (10/8 prefix)
		//     172.16.0.0      -   172.31.255.255  (172.16/12 prefix)
		//     192.168.0.0     -   192.168.255.255 (192.168/16 prefix)
		return ip4[0] == 10 ||
			(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
			(ip4[0] == 192 && ip4[1] == 168)
	}
	// Following RFC 4193, Section 8. IANA Considerations which says:
	//   The IANA has assigned the FC00::/7 prefix to "Unique Local Unicast".
	return len(ip) == IPv6len && ip[0]&0xfe == 0xfc
}

这个函数就排除了 ULA 地址被判断为 Global Routable 地址的问题。看起来 IPv4 也会有这样的问题。 可能是我一直以来的误解吧 🙄,得找个机会认真读读 RFC。网络真复杂。🥵

ChatGPT 的回答🥵:

  • IPv6 地址 fdd7:ca06:... 是全局的还是本地的?

  • IPv6 地址 fdd7:ca06:... 是一个全局单播地址。IPv6 地址的范围可以根据前缀来确定。一般来说,全局单播地址用于在 Internet 上进行通信,而本地地址则用于特定范围内的通信。

    IPv6 地址的前缀通常会指示地址的类型,例如:

    • 全局单播地址:通常具有全球唯一的前缀,用于在全球 Internet 上寻址和通信。
    • 链路本地地址:用于特定链路或局域网内的通信,范围仅限于特定的链路。
    • 唯一本地地址:用于私有网络内部通信,并不直接路由到 Internet。

    根据您提供的IPv6地址 fdd7:ca06:...,它的前缀并不是私有的或保留的本地地址前缀,因此它被认为是一个全局单播地址,用于 Internet 上的通信。

我为什么没有用请求外部站点的方式获取我的对外 IPv6 地址?

  • VPS 不支持 IPv6,解析了也没有用,反正会影响速度,国内也一并使用 IPv4 了
  • 为了使用 ChatGPT,我禁用了 IPv6 的解析

我目前拿 IPv6 的作用就一个:“回家”(参见:我的家庭网络拓扑结构)。

标签:IPv6 · DDNS