Efficient way to store IPv4/IPv6 addresses

evelina picture evelina · Oct 23, 2014 · Viewed 7.6k times · Source

I am working on a C/C++ networking project that it should be able to both use the IPv4 and IPv6 networking stacks. The project works only on Linux. So, I tried to find an efficient way to store the IP addresses and differentiate between the protocol families. The first approach was to have a union:

struct ip_addr {
   uint8_t fam; // socket family type
   union {
       struct in_addr ipv4_sin_addr;
       struct in6_addr ipv6_sin_addr;
   }addr;
};

The second approach was to define a typedef std::vector<unsigned char> IPAddressNumberand make the difference after the number of bytes from the vector.

The third approach was to use int128_t/uint128_t or __int128_t from gcc.

For this last case, I would like to know from which version of GCC these types are supported, for which platforms (especially IA-32/IA-64) and also if there are any known bugs. Also, which of the above solutions might be the most convenient one?

Answer

πάντα ῥεῖ picture πάντα ῥεῖ · Oct 23, 2014

As stated in the 1st answer 128 bit integer support is available since GCC 4.6.4.

Your problem isn't 128 bit integer support, but how to represent an IPv6 address correctly.
The correct answer for this, is to use the struct definitions available from the socket API.

These are available and standardized for various operating system implementations of the IPv6 stack.
Also you don't need to worry about efficiency using these. Alignment packing will do its work properly, and you don't have to care about endianess vs network byte order issues of the actual representation.


As for your edits:

You don't have to reinvent the wheel! There are already appropriate struct definitions available respecting the AF_xxx family type correctly.

Check these resources for more detailed explanations:

We have an IpAddr class in production, that uses the opaque sockaddr_in* and sockaddr_in6* pointers to encapsulate either an IPv4 or IPv6 address based on a sockaddr* pointer, and reinterpret_cast<> based on the sa_family member of the structure.