-1

I want to save data in a database using a canonicalized IP address.

The software, at this point, may get IPv4 or IPv6 addresses. I want to canonicalize by converting all addresses to an IPv6 address.

I know of inet_pton() to convert the string to a binary address. So I can try once with IPv6 and if that fails, try again with IPv4:

struct sockaddr_in6 sa;
int r = inet_pton(AF_INET6, input_addr, &sa.sin6_addr);
if(r != 1)
{
    // try again with IPv4
    struct sockaddr_in sa4;
    int r = inet_pton(AF_INET, input_addr, &sa4.sin_addr);

    // is there such a function?!
    inet_convert_ipv4_to_ipv6(&sa4, &sa);
}
char output_addr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &sa, output_addr, sizeof(output_addr));

// in C++, I can do that if function returns std::string
// in C you cannot return a pointer on the stack, watch out!
return output_addr;

So, I am looking for a function such as inet_convert_ipv4_to_ipv6(&sa4, &sa); to convert the sockaddr_in to a sockaddr_in6. Do we have such in the C library?

If we do not have such a function, would a solution be to use the '::ffff:' introducer as in:

// try again with IPv4
std::string ipv4_to_ipv6("::ffff:");
ipv4_to_ipv6 += input_addr;
int r = inet_pton(AF_INET6, ipv4_to_ipv6.c_str(), &sa.sin_addr);

I would hope that we have a way to convert an IPv4 sockaddr_in to an IPv6 sockaddr_in6 function instead. I think it would be cleaner... I think that this last piece of code is not 100% secure (I guess I can test the IPv4 conversion first and if it succeeds, then use the IPv6 conversion to make it secure. What a waste of time though!)

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
  • I think you have more to worry about, for instance [tag:c] is not the same as [tag:c++] and you just tagged your question with [tag:c] only while having a `std::string` there. This `std::string ipv4_to_ipv6("::ffff:");` is invalid [tag:c] syntax. – Iharob Al Asimi Oct 02 '15 at 02:07
  • I'm NOT looking for a C++ solution. Only a C function which does that conversion, hence my tagging. Now only people interested in C++ will give me stupid answers. Not good either. – Alexis Wilke Oct 02 '15 at 02:18
  • But then, why are you using c++? If you want a c solution write c code. And also, I guess that using a `std::string` for a string constant is not very efficient. – Iharob Al Asimi Oct 02 '15 at 02:39
  • You are aware that security precautions are necessary with the IPv4-mapped IPv6 addresses, right? Review [Section 2.2](https://tools.ietf.org/html/rfc4942#section-2.2) of [RFC 4942: IPv6 Transition/Coexistence Security Considerations](https://tools.ietf.org/html/rfc4942). In particular, the last paragraph of that section cautions against doing this. From a security standpoint, IPv4 alone is potentially more secure. It's your choice, but personally I'd err on the side of caution and use an IPv4 address with `AF_INET` instead of `AF_INET6`. That being said, no such standard function exists. –  Oct 02 '15 at 02:47
  • @iharob The bit of C++ here is negligible in my opinion since the question is specifically about a C API. This is one of the few instances I would say that tagging a question with C++ in the code as C is acceptable since there are only two lines of C++ code, the rest all being valid due to the use of the C API in question. –  Oct 02 '15 at 02:57
  • 1
    @ChronoKitsune, note that I clearly say I want to use that as a unique key in my database and not as a way to allow transactions over a socket. That being said, it is a good thing to know of the "short coming" of IPv6. From what I understand, though, if you properly setup your IPv6 firewall, you should be just fine. You just have to remember to block all IPv4 (or most) when doing that work. – Alexis Wilke Oct 02 '15 at 04:11
  • @AlexisWilke Pardon my mistake then as I clearly didn't read the full question. I was rather alarmed when my attention was drawn to your code, and I read from that point forward to get an idea of what you wanted to do. I didn't realize you weren't actually doing anything with those IP addresses over the wire. :-) –  Oct 02 '15 at 04:41
  • Neither the C nor the C++ standard cover IP addresses, not even IPv4. You probably want to state the OS. I don't think POSIX covers IPv6 yet. – MSalters Oct 02 '15 at 11:31
  • @MSalters, `inet_pton()` and other similar functions are POSIX.1-2001... – Alexis Wilke Oct 02 '15 at 19:31
  • 1
    Since the IPv4-Mapped addresses (`::ffff:0:0/96`) are not routable on the Internet, I wouldn't put a lot of effort into blocking them from the Internet, and, certainly, not try to use them out to the Internet. – Ron Maupin Oct 02 '15 at 19:45
  • Since you wish to have a canonical IPv6 representation, you can refer to RFC 5952, A Recommendation for IPv6 Address Text Representation. "_This document defines a canonical textual representation format._" https://tools.ietf.org/html/rfc5952 – Ron Maupin Oct 02 '15 at 21:36
  • @RonMaupin, actually since I am using Cassandra, I can use the binary IP address instead of the text version. Then text canonicalization is less important. – Alexis Wilke Oct 02 '15 at 22:08
  • 1
    It would seem to me, based on your comments, that you will not try to use these addresses to send data, only as indices, you should just append 96 bits of `0` to the front of the IPv4 address, thereby creating a deprecated IPv4-Compatible IPv6 address.. That should be simple enough to do without needing to find a library function. The resulting IPv6 address cannot actually be used for anything, and anything that reads it from the database can assume an address that starts with 96 bits of `0` is an IPv4 address. – Ron Maupin Oct 02 '15 at 22:13

2 Answers2

1

You could keep using sockaddr_in6 in both calls to inet_pton. When you convert the IPv4 successfully, you could do a shift to the correct place. Here is a example of that in map4to6() method.

EDIT: To fix the wrong method name mentioned.

Ricardo Lucca
  • 158
  • 3
  • 10
  • It looks like they are saving the 4 bytes of an IPv4 as the last 4 bytes of an IPv6 which I don't think is correct. But I was more hoping for a system function so if something changes (very unlikely now I would imagine...) then the software continues to work. – Alexis Wilke Oct 02 '15 at 04:00
  • Actually it looks like they properly save the IPv4 in the address. Only the specific function you reference does not clearly show that... – Alexis Wilke Oct 02 '15 at 07:30
  • @AlexisWilke, I showed the wrong function. The correct one is map4to6. The shift the data from the start (in_addr) to the end (in6_addr). But the shift alone is not possible because need set others two bytes. I'll edit to fix that. – Ricardo Lucca Oct 02 '15 at 10:54
  • @AlexisWike Another question why you need it? I mean if you put a server to listen in a socket using IPv6 by default you will accept all IPv4 communications. Right? – Ricardo Lucca Oct 02 '15 at 11:18
  • I just want a canonicalized key for my database. So in this case it is not used over the wire... – Alexis Wilke Oct 02 '15 at 19:15
1
unsigned char *ip = in6_addr.s6_addr;
((uint64*)ip)[0] = 0;
((uint16*)ip)[4] = 0;
ip[10] = 0xFF;
ip[11] = 0xFF;
((in_addr_t*)(ip + 12))[0] = in_addr.s_addr
nomad li
  • 11
  • 4