9

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 IPAddressNumber`and 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?

sigjuice
  • 28,661
  • 12
  • 68
  • 93
evelina
  • 157
  • 1
  • 1
  • 8
  • 1
    I'm not sure that `int128_t` is needed or suitable for IPv6 addresses. Posix gives other types in its API. For GCC, better use the latest version (GCC 4.9.1 in october 2014) and use C++11 if possible. – Basile Starynkevitch Oct 23 '14 at 15:27
  • 3
    I'd also recommend to use the [`struct sockaddr_in6`](http://man7.org/linux/man-pages/man7/ipv6.7.html) structure to deal with IPv6 addresses. – πάντα ῥεῖ Oct 23 '14 at 15:42
  • You'll rarely use IP addresses as real numbers (besides they'll need to be provided in network byte order). So again: Using a `uint128_t` for representation of an IPv6 address is probably a **very bad idea**! – πάντα ῥεῖ Oct 23 '14 at 16:41
  • [What should we do with a XY problem question, where X can be answered?](http://meta.stackoverflow.com/questions/275182/what-should-we-do-with-a-xy-problem-question-where-x-can-be-answered) – πάντα ῥεῖ Oct 23 '14 at 16:59
  • @πάντα ῥεῖ Thank you for your quick answers! I am afraid I haven't given enough details about the project I am working at. I will edit my post and add more information. – evelina Oct 23 '14 at 18:59
  • @AziNuAmChef What actually makes you believe, that a 128 bit integer would be a better representation than `struct sockaddr_in6`? Can you elaborate on this, to improve your question please? – πάντα ῥεῖ Oct 23 '14 at 19:03
  • @πάντα ῥεῖ I have just finished editing. – evelina Oct 23 '14 at 19:12
  • `typedef std::vector IPAddressNumber` I'd prefer `typedef std::array IPAddressNumber`. – πάντα ῥεῖ Oct 23 '14 at 19:16
  • @AziNuAmChef I have added some more information to my answer. – πάντα ῥεῖ Oct 24 '14 at 19:02
  • 1
    `struct sockaddr_storage` can be used for family agnostic storage, it is convenient, by definition not bit-frugal. – Steve-o Oct 25 '14 at 16:33

3 Answers3

10

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.

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • I actually saw an open source project (chromium) that uses a similar representation with the one mentioned by you: it uses a struct called SockaddrStorage [1] and a class named IPEndPoint that works with this struct. The problem with this struct is that it is useful when you have also a port or you intend to create a socket(eg: bind , connect calls) and not just for storing the ip. [1] http://goo.gl/hSyW8o – evelina Oct 25 '14 at 16:30
  • I need an internal way to store the ip after converting it from a string representation(ie: from a flag or an environment variable). I find more useful the representation with struct ip_addr because it is more convenient for calls like inet_ntop/inet_pton It is not possible to use sockaddr (alone and not as a pointer) because the sa_data field is not big enough to store an IPv6 address. – evelina Oct 25 '14 at 16:33
  • @evelina _"I need an internal way to store the ip after converting it from a string representation(ie: from a flag or an environment variable)."_ Well, I don't understand, what you're actually bothering about _efficiency_. The mentioned structures will well represent the IP protocol dependent types. – πάντα ῥεῖ Oct 25 '14 at 19:20
  • Nice answer. Using it. – Drew Oct 24 '16 at 23:40
4

According to this thread __int128_t and __uint128_t were introduced somewhere around version 4.2. And according to GCC's documentation __int128 and its unsigned counterpart unsigned __int128 are supported since GCC 4.6.4.

Note that an unportable 128-bit integer is definitely not the appropriate type to save and work with IPv6 addresses. As mentioned in the comments there are special data structures for this, like sockaddr_in6.

Community
  • 1
  • 1
Columbo
  • 60,038
  • 8
  • 155
  • 203
0

This code is from nginx. It's the same as 1st answer. I don't think it's a good idea to use pointer sockaddr_in* .

typedef union {
    struct sockaddr           sockaddr;
    struct sockaddr_in        sockaddr_in;
#if (NGX_HAVE_INET6)
    struct sockaddr_in6       sockaddr_in6;
#endif
#if (NGX_HAVE_UNIX_DOMAIN)
    struct sockaddr_un        sockaddr_un;
#endif
} ngx_sockaddr_t;

https://github.com/nginx/nginx/blob/912fb44e25c6ab2598e36b4544c709b871251b2e/src/core/ngx_inet.h#L44

along
  • 21
  • 3