golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.98k stars 17.67k forks source link

net/netip: Is4In6 should return true for well-known prefixes used in IPv4/IPv6 translation #65635

Open JenAkaFurry opened 9 months ago

JenAkaFurry commented 9 months ago

Go version

go1.22-20240109-RC01 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/usr/local/google/home/furry/.cache/go-build'
GOENV='/usr/local/google/home/furry/.config/go/env'
GOEXE=''
GOEXPERIMENT='fieldtrack,boringcrypto'
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/usr/local/google/home/furry/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/usr/local/google/home/furry/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/google-golang'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/lib/google-golang/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22-20240109-RC01 cl/597041403 +dcbe772469 X:fieldtrack,boringcrypto'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='clang'
CXX='clang++'
CGO_ENABLED='0'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3244063302=/tmp/go-build -gno-record-gcc-switches'

What did you do?

https://go.dev/play/p/FwpGyqH1hio

package main

import ( "fmt" "net/netip" )

func main() {

// An IPv4-mapped IPv6 Address (RFC4291).
ipv4Mapped := "::ffff:192.0.2.1"

// An IPv6 address synthesized from a "Well-Known Prefix" (64:ff9b::/96, RFC6052)
nat64WKP := "64:ff9b::192.0.2.1"

// An IPv6 address synthesized from a Local-Use IPv4/IPv6 Translation Prefix
// (64:ff9b:1::/48, RFC8215) and 192.0.2.1
rfc8215WKP := "64:ff9b:1::192.0.2.1"

// Printing the addresses in their canonical text representation,
// rfc5952#section-5
fmt.Printf("IPv4-mapped address: %s\n", netip.MustParseAddr(ipv4Mapped).String())
fmt.Printf("NAT64 address: %s\n", netip.MustParseAddr(nat64WKP).String())
fmt.Printf("RFC8215 address: %s\n", netip.MustParseAddr(rfc8215WKP).String())

}

What did you see happen?

The output:

IPv4-mapped address: ::ffff:192.0.2.1 NAT64 address: 64:ff9b::c000:201 RFC8215 address: 64:ff9b:1::c000:201

Only the first address is printed in mixed notation (https://datatracker.ietf.org/doc/html/rfc5952#section-5). That indicates that Is4In6() only recognizes ::ffff:0:0/96 as a well-known prefix, used for embedding IPv4 addresses into IPv6 ones.

Both 64:ff9b::/96 (RFC6052) and 64:ff9b:1::/48 (RFC8215) prefixes are not recognized as used for having IPv4 addresses embedded in the lower 32 bits.

As both prefixes are well-known and reserved by IANA (https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml), I believe Is4In6() shall be updated (happy to write that code, indeed).

P.S. I'd also argue that for backwards compatibility it would be beneficial to recognize ::/96 as a Well-Known Prefix, despite its deprecation, as there are still systems around which might be using it.

What did you expect to see?

All 3 addresses shall be represented using mixed notation, as per https://datatracker.ietf.org/doc/html/rfc5952#section-5

"mixed notation is RECOMMENDED if the following condition is met: the address can be distinguished as having IPv4 addresses embedded in the lower 32 bits solely from the address field through the use of a well-known prefix. Such prefixes are defined in [RFC4291] and [RFC2765] at the time of this writing."

dr2chase commented 9 months ago

@ianlancetaylor @neild if you could give this a look.

favonia commented 1 month ago

My 2 cents: I like this proposal to improve the output, but I hope we can keep the current behavior of Is4In6 for compatibility. Maybe we can create a new function that checks all well-known prefixes?

JenAkaFurry commented 1 month ago

My 2 cents: I like this proposal to improve the output, but I hope we can keep the current behavior of Is4In6 for compatibility. Maybe we can create a new function that checks all well-known prefixes?

I do not have any strong opinion on that, I guess we can create a new function and mark Is4In6() as deprecated. Any preference for the name? How about 'Is4EmbeddedIn6()'?

favonia commented 1 month ago

I believe that the current Is4In6 should be kept and should not be deprecated. IPv4-mapped IPv6 addresses are essentially IPv4 addresses represented in IPv6 format, without any additional significance. They are, in essence, IPv4 addresses, and many systems will treat them as such. For instance, sending a packet with an IPv4-mapped IPv6 address on a dual-stack machine will result in an IPv4 packet being sent. Therefore, Is4In6 is valuable in determining whether an IPv4 packet will be transmitted. It is also useful for checking whether an IPv6 address is suitable for AAAA records in DNS for the same reason.

This distinction is crucial when compared to other protocols, which aim to send genuine IPv6 packets with an embedded address. The need for a different prefix (e.g., Well-Known Prefix) in these cases arises from this fundamental difference. For example, IPv4-embedded IPv6 addresses are designed to be used in AAAA records, because genuine IPv6 packets will be sent, while IPv4-mapped ones should never appear in them, because only IPv4 packets will be sent. Therefore we should avoid conflating IPv4-mapped IPv6 addresses with other IPv4-translatable or IPv4-embedded IPv6 addresses. Personally I'm fine if we wish to modify String to handle all special prefixes defined by RFCs, but there should be a clear way to differentiate between them.


PS: I know the SIIT protocol messed up the situation a little bit, but my main point stands. PPS: I'm also fine if we want to introduce variants of Unmap, but we should probably keep the current Unmap for backward compatibility.

gopherbot commented 1 month ago

Change https://go.dev/cl/617835 mentions this issue: net/netip: clarify Addr.Is4In6 documentation

neild commented 1 month ago

netip.Addr.Is4In6 is documented as reporting whether an address is "an IPv4-mapped IPv6 address".

RFC 4291, section 2.5.5.2 defines an IPv4-mapped IPv6 address as in the subnet ::ffff:0:0/96. RFC 5156, section 2.2 has a more concise definition.

Since "IPv4-mapped IPv6 address" is a term of art with a specific definition, Is4In6's current behavior is correct and should not change. https://go.dev/cl/617835 makes the Is4In6 documentation more explicit.

A separate question is whether we should use mixed notation when formatting RFC6052 and RFC8215 addresses. That seems like it would be reasonable, but I haven't thought about it in detail.