Open database64128 opened 2 years ago
I don't think we want to add API for this. We're trying to fix the real problem (ReadMsgUDPAddrPort etc returning bad netip.Addr/AddrPort values) instead in #54234
I wouldn't call this "the real problem" or "bad values". It is natural for ReadMsgUDPAddrPort calls on IPv6 UDP sockets to return IPv4-mapped IPv6 addresses, because that's how the underlying socket works. A method like UnmappedEquals
allows us to conveniently compare the returned address without changing the address family.
The following benchmark uses AddrPortMappedEqualUnsafe
to simulate the proposed UnmappedEquals
.
// AddrPortMappedEqual returns whether the two addresses point to the same endpoint.
// An IPv4 address and an IPv4-mapped IPv6 address pointing to the same endpoint are considered equal.
// For example, 1.1.1.1:53 and [::ffff:1.1.1.1]:53 are considered equal.
func AddrPortMappedEqual(l, r netip.AddrPort) bool {
if l == r {
return true
}
return l.Port() == r.Port() && l.Addr().Unmap() == r.Addr().Unmap()
}
type addrPortHeader struct {
ip [16]byte
z unsafe.Pointer
port uint16
}
func AddrPortMappedEqualUnsafe(l, r netip.AddrPort) bool {
lp := (*addrPortHeader)(unsafe.Pointer(&l))
rp := (*addrPortHeader)(unsafe.Pointer(&r))
return lp.ip == rp.ip && lp.port == rp.port
}
var (
addrPort4 = netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 1080)
addrPort4in6 = netip.AddrPortFrom(netip.AddrFrom16([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 127, 0, 0, 1}), 1080)
)
func BenchmarkAddrPortMappedEqual(b *testing.B) {
b.Run("Equal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
AddrPortMappedEqual(addrPort4, addrPort4)
}
})
b.Run("NotEqual", func(b *testing.B) {
for i := 0; i < b.N; i++ {
AddrPortMappedEqual(addrPort4, addrPort4in6)
}
})
}
func BenchmarkAddrPortMappedEqualUnsafe(b *testing.B) {
b.Run("Equal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
AddrPortMappedEqualUnsafe(addrPort4, addrPort4)
}
})
b.Run("NotEqual", func(b *testing.B) {
for i := 0; i < b.N; i++ {
AddrPortMappedEqualUnsafe(addrPort4, addrPort4in6)
}
})
}
BenchmarkAddrPortMappedEqual/Equal-4 490546808 2.446 ns/op 0 B/op 0 allocs/op
BenchmarkAddrPortMappedEqual/NotEqual-4 250462476 4.730 ns/op 0 B/op 0 allocs/op
BenchmarkAddrPortMappedEqualUnsafe/Equal-4 1000000000 0.4085 ns/op 0 B/op 0 allocs/op
BenchmarkAddrPortMappedEqualUnsafe/NotEqual-4 1000000000 0.3842 ns/op 0 B/op 0 allocs/op
In configuration and other places, IPv4 addresses are usually specified unmapped, like
1.1.1.1:53
. When dealing withReadFromUDPAddrPort
andReadMsgUDPAddrPort
, the returnednetip.AddrPort
is likely to be an IPv4-mapped IPv6 address, like[::ffff:1.1.1.1]:53
, depending on how the UDP socket was opened.To check whether these 2 addresses point to the same endpoint, we would have to do something like:
It'd be nice if we have a method on
netip.AddrPort
to directly compare the unexportedaddr
field innetip.Addr
. I propose we add something like:The method can also be called
MappedEquals
if #54365 is accepted./cc @bradfitz