net.IP type

639 views
Skip to first unread message

Joshua Liebow-Feeser

unread,
Oct 9, 2014, 2:13:09 PM10/9/14
to golan...@googlegroups.com
I've been working with net.IP a lot recently, and I've got some questions about the design choices behind it, and generally I'd like to have a discussion about it (because discussions are interesting, and grokking is fun, and whatnot).

net.IP is defined as:

type IP []byte

This allows that IPv4 addresses (which are 4 bytes long) and IPv6 addresses (which are 16 bytes long) can be represented using the same type. My guess is that the reason for doing this is that it allows the other types in the package which contain an IP address (such as UDPAddr and TCPAddr) and functions which accept or return IP addresses don't have to be duplicated to take separate IPv4 and IPv6 address types.

However, this introduces a lot of really annoying behavior. It means, first off, that two addresses cannot be compared for equality using "==". Second, it means that IP addresses cannot be used as may keys. Third, it means that code cannot be written which, by type contract, expects solely IPv4 or solely IPv6 addresses. Fourth, it means that if one or the other type is expected, this contract is not enforced, and it requires the programmer to either introduce an error condition or a panic condition (for example, by calling addr.To4() or addr.To6(), which respectively convert the addr to the given form, and return nil if addr is not the corresponding version - using the address would then panic if it was originally the wrong type since it'd be nil after calling addr.ToN()). Fifth, if all of these explicit checks are not performed, it can introduce annoying bugs (for example, the code I've been writing recently has often accidentally propagated addresses "0.0.0.0" since I neglected to explicitly call addr.To4(), meaning that when I accessed the first four bytes of addr, they were 0, since addr was 16 bytes and the 4 bytes of the address were stored from bytes 11 through 15). Sixth, it means that, since addresses are passed by reference, they cannot be safely stored without being copied for fear that, after returning, the caller may modify the underlying bytes (or vice versa for the callee modifying the bytes during the call)

As I see it, a much nicer way of addressing the concern that types and functions need to be able to accept arbitrary IP types is to have IPv4, IPv6, and IP types, something like this:

type IP interface {
    IPVersion() int // Or "IPv4() bool" or whatever
}

type IPv4 [4]byte

func (i IPv4) IPVersion() int { return 4 }

type IPv6 [16]byte

func (i IPv6) IPVersion() int { return 6 }

This way, you could still have types like UDPAddr and TCPAddr simply have an IP field, but you wouldn't have to worry about any of the problems I mentioned above.

The questions I'm asking, then, are:
- Does anybody disagree with my characterization of the challenge (that is, the need to have multiple types of IP addresses in a single type for simplicity's sake)?
- Does anybody disagree with my characterization of the problems that the current solution introduces?
- Does anybody think that my proposed solution fails to address the challenge in some way, or introduces some other problems?
- Any other thoughts, feedback, or hate mail?

Cheers,
Josh

Andrew Bursavich

unread,
Oct 9, 2014, 8:26:28 PM10/9/14
to golan...@googlegroups.com
I've spent a little time working with net.IP and didn't run into too many challenges.  I would suggest that your public API should support both IPv4 and IPv6.  Most of my code was agnostic to the version; where it wasn't I checked.  When I needed to guarantee that a callee didn't alter my slice I returned a copy.

Ultimately, there's no way net.IP will change in go1.


Andy

Joshua Liebow-Feeser

unread,
Oct 9, 2014, 8:42:32 PM10/9/14
to Andrew Bursavich, golan...@googlegroups.com

Oh, yeah I know. This less of a proposed change than a curiosity. And this was an IPv4-specific application.

> --
> You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/Ian5SvVRFCY/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Mikio Hara

unread,
Oct 9, 2014, 8:44:32 PM10/9/14
to Joshua Liebow-Feeser, golang-nuts
On Fri, Oct 10, 2014 at 3:13 AM, Joshua Liebow-Feeser
<josh...@gmail.com> wrote:

> The questions I'm asking, then, are:
> - Does anybody disagree with my characterization of the challenge (that is,
> the need to have multiple types of IP addresses in a single type for
> simplicity's sake)?

i guess that net.IP is just designed for conveying 32 or 128-bit bit
sequence from/to the protocol stack inside the kernel, nothing less or
more.

> - Does anybody disagree with my characterization of the problems that the
> current solution introduces?

you can make a new type and extend it for your purpose; e.g., type
IPv4 net.IP, func (ip IPv4) Compare(IPv4) bool. fwiw,
http://godoc.org/github.com/mikioh/ipaddr.

Joshua Liebow-Feeser

unread,
Oct 9, 2014, 11:30:46 PM10/9/14
to Mikio Hara, golang-nuts
Yes, I could do that, but it wouldn't solve any of the issues I mentioned. You'd still have no guarantee from the type system that you were getting an IPv4 rather than IPv6 address (or vice versa), it'd still be a reference type, you'd still have to perform dynamic checks and conversions, etc.

Mikio Hara

unread,
Oct 10, 2014, 12:39:29 AM10/10/14
to Joshua Liebow-Feeser, golang-nuts
On Fri, Oct 10, 2014 at 12:30 PM, Joshua Liebow-Feeser
<josh...@gmail.com> wrote:

> Yes, I could do that, but it wouldn't solve any of the issues I mentioned.

i mean, making new types (in your case two array types and an
interface type) makes sense when you're sure what's the IPv4 address.
not sure whether you need to treat ipv6 ipv4-mapped address or
ipv4-embedded ipv6 address for translators as IPv4 address.

Joshua Liebow-Feeser

unread,
Oct 10, 2014, 12:47:49 AM10/10/14
to Mikio Hara, golang-nuts
Except I'm pretty sure that IPv4 and IPv6 addresses are basically unrelated, right? They're different address spaces. I think there may be some scheme for embedding IPv4 addresses in an IPv6 scheme as a stopgap, but it's not just "use an IPv4 address as an IPv6 address." For the most part, it's like conflating IP addresses and MAC addresses.

Mikio Hara

unread,
Oct 10, 2014, 1:01:19 AM10/10/14
to Joshua Liebow-Feeser, golang-nuts
On Fri, Oct 10, 2014 at 1:46 PM, Joshua Liebow-Feeser
<josh...@gmail.com> wrote:

> Except I'm pretty sure that IPv4 and IPv6 addresses are basically unrelated,
> right?

yup, as i said above net.IP carries 32 or 128-bit bit sequences
from/to the protocol stack.

> but it's not just "use an IPv4 address as an IPv6 address.

you are free to blame us for the terrible leaky abstractions. ;) the
net package currently uses ipv6 ipv4-mapped address to enable
IPV6_V6ONLY socket option.

Joshua Liebow-Feeser

unread,
Oct 10, 2014, 1:09:56 AM10/10/14
to Mikio Hara, golang-nuts
Hahaha fair enough.

Nick Craig-Wood

unread,
Oct 13, 2014, 11:10:49 AM10/13/14
to Joshua Liebow-Feeser, golan...@googlegroups.com
On 09/10/14 19:13, Joshua Liebow-Feeser wrote:
> I've been working with net.IP a lot recently, and I've got some
> questions about the design choices behind it, and generally I'd like to
> have a discussion about it (because discussions are interesting, and
> grokking is fun, and whatnot).
>
> net.IP is defined as:
>
> type IP []byte
>
> This allows that IPv4 addresses (which are 4 bytes long) and IPv6
> addresses (which are 16 bytes long) can be represented using the same
> type. My guess is that the reason for doing this is that it allows the
> other types in the package which contain an IP address (such as UDPAddr
> and TCPAddr) and functions which accept or return IP addresses don't
> have to be duplicated to take separate IPv4 and IPv6 address types.

I always assumed that this was to mirror the `struct sockaddr` used in
the BSD socket interface, where the address is just a string of
characters. Here is the original BSD definition (which incidentally is
too short for an IPv6 address, but you get the idea). You set what type
of address it is in sa_family and the address string in sa_data.

struct sockaddr
{
unsigned short int sa_family;
unsigned char sa_data[14];
};

--
Nick Craig-Wood <ni...@craig-wood.com> -- http://www.craig-wood.com/nick
Reply all
Reply to author
Forward
0 new messages