-
Notifications
You must be signed in to change notification settings - Fork 39
FromStdIP has surprising behavior with a v4 net.IP represented at 16 bytes #34
Comments
This is actually especially bad when using FromStdIPNet, because if the net.IPNet's IP is in the wrong form, you have to do a ton of legwork to pull apart the net.IPNet, fix up the IPs and mask, then reconstruct the net.IPNet and feed that to FromStdIPNet :( |
Unfortunately I think this might be the expected behavior. While it is a nuisance that the stdlib defaults to 16 bytes for e.g. The caller is able to explicitly pass That said, I am not opposed to assuming the caller wants the standard 4-byte representation for IPv4 and requiring an explicit method call to opt in to the mapped representation. We'd just want to document it well. |
One clarification question: I assume Tailscale folks are optimizing for minimal memory use in constrained environments? I am focused more on API ergonomics since I am running this on pretty much all amd64 machines. But I would be in favor of picking the smallest representation as the default and allowing the caller to opt in to the larger representation if need be. |
In this case, I was optimizing for not breaking Tailscale :) If you pass a v6 mapped v4 into netlink, it gets configured as an IPv6 address, not an IPv4 address, and your routing, uh, stops working. Prior to the conversion to netaddr, we were passing around ipv4 addresses, but the way I had to apply the translation into netaddr accidentally created a v6 mapped address instead. This is the impedance mismatch between the stdlib and netaddr rearing its head: if you I don't think we care explicitly about memory usage, although obviously we'd prefer to use fewer bytes for IPv4. But the way netaddr handles v6-mapped v4 means that it's really not an IPv4 at all without careful explicit handling, and the FromStd* helpers currently implement the more surprising behavior (imho). I think I'd be okay with FromStd* not creating mapped IPs, and having a different way to request a mapping that includes mapped v4 addresses. It's not ideal, but we need a way to resolve the ambiguity created by the net.IP type. |
@danderson, I think you missed IP.Unmap: https://godoc.org/inet.af/netaddr#IP.Unmap
We could add an Unmap method on IPPort too. |
btw, the code and bug I introduced are open source in this case:
|
Indeed, I missed Unmap. AFAICT, I will have to use |
Adjusted our conversion code: tailscale/tailscale@48b1e85...e16f7e4 Another possible API: Now the conversion I'm doing in the tailscale code is just Thoughts? |
I'm fine changing I'm thinking something like Other possible names:
Also, there is no |
Just encountered this in Tailscale, converting from net.IP into netaddr.IP for one of our libraries.
If you give FromStdIP an IPv4 address that currently happens to be in its 16-byte internal representation, FromStdIP will create a v6 mapped v4 netaddr.IP. Because of net.IP's whacky treatment of pure v4 vs. mapped v4, this requires a fairly clunky pattern to use FromStdIP when you want the intuitive behavior:
Maybe FromStdIP should canonicalize the net.IP before converting it? That would prevent people from deliberately creating a v6 mapped v4 netaddr.IP from a net.IP, but that seems like a bit of a corner case - and one we could address with helpers like netaddr.To4in6(), for cases where you deliberately want a v6 address representing a v4 IP.
cc @mdlayher @bradfitz thoughts?
The text was updated successfully, but these errors were encountered: