2
votes

I am attempting to use an IPv6 socket to connect to an IPv4 address using an IPv4 mapped IPv6 address, on linux (debian-lenny-64 2.6.26-2-amd64)

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

int main(int argc, void **argv)
{
    struct addrinfo *sa;
    struct addrinfo *ra;

    int err = getaddrinfo("2001:DB8::2", 0, 0, &sa);

    int fd = socket(sa->ai_family, SOCK_DGRAM, 0);

    int v6only = 0;     

    err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&v6only, sizeof(v6only)); 

    err = bind(fd, sa->ai_addr, sa->ai_addrlen);

    err = getaddrinfo("::ffff:192.168.0.1", "9", 0, &ra);

//  err = getaddrinfo("2001:DB8::1", "9", 0, &ra);
//  err = getaddrinfo("::ffff:127.0.0.1", "9", 0, &ra);

    err = connect(fd, (struct sockaddr *)ra->ai_addr, sizeof(struct sockaddr_in6));
}

(I've removed error testing from the pasted code)

2001:DB8::2 and 192.168.0.2 are local addresses (both on the same interface).
2001:DB8::1 and 192.168.0.1 are the remote addresses (both on the same interface).

I change the remote address for the connect call and get the following:

  • connect to ::ffff:127.0.0.1 success (localhost)
  • connect to 2001:DB8::1 success (remote IPv6 address)
  • connect to ::ffff:192.168.0.2 success (local IPv4 address)
  • connect to ::ffff:192.168.0.1 failure (22 Invalid argument - remote IPv4)

If I change to doing IPv4 connects then the connects also work.

I think there must be an issue with routing somewhere but I'm at a loss to work out what I need to change. Firstly should I in theory be able to do this?
Any ideas what is going wrong?

1
I'm not sure why the local IPv4 addresses (::ffff:127.0.0.1 and ::ffff:192.168.0.2) work, but I'm not surprised that the remote IPv4 address doesn't work. You are binding the socket to a local IPv6 address before connecting to a remote IPv4 address. That can't work... What if you just omit the bind() call?Celada
It's a test to work out which of our listening addresses we should include in a message to the far end. We're potentially listening on multiple addresses in a multihomed system. We want to make sure we use the appropriate address as the source for outbound communication as responses need to come back to something we're listening on. If none of them are appropriate then we don't send (but I would have expected the failing case to work). It works for IPv4->IPv4 and IPv6->IPv6 situations but when I try IPv4 mapped IPv6 addresses it goes wrong.superrobot
If you bind() to an IPv4 address when you connect() to an IPv4 address, and bind() to an IPv6 address when you connect() to an IPv6 address, then I suppose it will work.Celada
Interestingly not calling bind() and connect() works for the remote ::ffff:192.168.0.1 address. I'll see if I can get some info from getsockname() for the calling side. Thanks.superrobot
When I don't bind() getsockname() tells me ::ffff:192.168.0.2 is the address of the socket. So that makes sense. Some internal magic means that 127.0.0.1 and local IPv4 addresses will work when you bind to IPv6. Once you attempt to go to a remote IPv4 address using an IPv6 socket you cannot bind to a local IPv6 address. Thanks for the help.superrobot

1 Answers

1
votes
  • See the function "getaddrinfo()" as
     ~$ man getaddrinfo 
    .
  • The 'hints' paramter of the function has attribute 'ai_flags', if you set it to 'AI_PASSIVE'. It can work for both IPv4 and IPv6 addressing modes.