arobinson
arobinson

Reputation: 166

Cannot Bind Socket to Port in Both IPv4 and IPv6

I am new to socket programming, and was working on some scratch code to get a better feel for it, when I hit a snag. Any guidance on what I am doing wrong would be much appreciated!

I am trying to write a simple program that binds and listens on a user specified port and sends a "Hello" message to any connection. For kicks, I figured I would just listen on the same port for all IPv4 and IPv6 addresses. Here is a code snippet:

memset(&hints, 0, sizeof hints);
hints.ai_family   = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags    = AI_PASSIVE;

if ( getaddrinfo(NULL, argv[1], &hints, &res) != 0 ) { 
    printf("getaddrinfo failed!\n");
    return 1;
}

for ( addrinfo* p = res; p != NULL; p = p->ai_next ) { 
    inet_ntop(p->ai_family, get_addr_ptr(p->ai_addr), ipstr, sizeof ipstr);
    printf("Found IP: %s\n",ipstr);

    printf("\tGetting socket...\t");
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if ( sockfd == -1 ) { 
        perror("\t\tError");
        continue;
    }
    printf("OK\n");

    printf("\tBind Socket to Port...\t");
    if ( bind(sockfd, p->ai_addr, p->ai_addrlen) == -1 ) { 
        perror("\t\tError");
        close(sockfd);
        continue;
    }
    printf("OK\n");

    printf("\tListen on socket...\t");
    if ( listen(sockfd, BACKLOG) == -1 ){
        perror("\t\tError");
        continue;
    }
    printf("OK\n");
}

freeaddrinfo(res);
while (1) {/* accept connections */}

When I run the code, I get this output:

$ ./simpleServer 8080
Found IP: 0.0.0.0
    Getting socket...       OK
    Bind Socket to Port...  OK
    Listen on socket...     OK
Found IP: ::
    Getting socket...       OK
    Bind Socket to Port...
        Error: Address already in use

However, if I look at netstat when the program is running, I don't see any conflicting port tied to ::, or any other IPv6 address for that matter.

I played a little bit more with this, and found that I can bind to the port with just IPv4 or just IPv6, but not both, which I don't understand. I have created two socket, with the following ai_addr's:

I feel like I am probably missing something fundamental, but I cant see it.

Thanks!

Upvotes: 3

Views: 6583

Answers (1)

Inrin
Inrin

Reputation: 322

The problem you're facing is probably that you're using Dual-Stack mode. At least on Linux (Others have it often disabled e.g. FreeBSD) specifying :: as address yields in binding to * as netstat or ss would put it. This means, it accepts both IPv6 and IPv4 Addresses. Though the IPv4 ones get mapped to ::ffff:<your normal ipv4 address>. So I'm guessing the same problem occurred to you.

You could use IPV6_V6ONLY socket option if af_family == AF_INET6 to not allow this behaviour.

IPV6_V6ONLY (since Linux 2.4.21 and 2.6) If this flag is set to true (nonzero), then the socket is restricted to sending and receiving IPv6 packets only. In this case, an IPv4 and an IPv6 application can bind to a single port at the same time. If this flag is set to false (zero), then the socket can be used to send and receive packets to and from an IPv6 address or an IPv4-mapped IPv6 address.

The argument is a pointer to a boolean value in an integer.

The default value for this flag is defined by the contents of the file /proc/sys/net/ipv6/bindv6only. The default value for that file is 0 (false).

Taken from man 7 ipv6

Please note that you're overriding your previously created/binded/listened socket. Also use gai_strerror to get a meaningful error from the return value of getaddrinfo.

Upvotes: 7

Related Questions