Binding Sockets to IPv6 Addresses
Asked Answered
K

2

8

I am trying to write a web server that listens on both IPv4 and IPv6 addresses. However, the code that I originally wrote did not work. Then I found out that the IPv6 structures work for both IPv4 and IPv6. So now I use the IPv6 structures however, only the IPv4 addresses work. This post, why can't i bind ipv6 socket to a linklocal address, which said to add server.sin6_scope_id = 5; so I did that but it still does not accept IPv6 telnet connections. Any help would be greatly appreciated because I am thoroughly stumped.
Thanks!

My code is below:

void initialize_server(int port, int connections, char* address)
{
        struct sockaddr_in6 socket_struct;
        /*Creates the socket*/
        if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
                syslog(LOG_ERR, "%s\n", strerror(errno));
                exit(EXIT_FAILURE);
        }/*Ends the socket creation*/

        /*Populates the socket address structure*/
                socket_struct.sin6_family = AF_INET6;

        if(address == NULL)
                socket_struct.sin6_addr=in6addr_any;
        else
        {
                inet_pton(AF_INET6, "fe80::216:3eff:fec3:3c22", (void *)&socket_struct.sin6_addr.s6_addr);
        }
        socket_struct.sin6_port =htons(port);
        socket_struct.sin6_scope_id = 0;
        if (bind(sock_fd, (struct sockaddr*) &socket_struct, sizeof(socket_struct)) < 0)
        {
                syslog(LOG_ERR, "%s\n", strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the binding.

        if (listen(sock_fd, connections) <0)
        {
                syslog(LOG_ERR, "%s\n", strerror(errno));
                exit(EXIT_FAILURE);
        }//Ends the listening function

}//ends the initialize server function.
Kyanize answered 22/11, 2012 at 2:6 Comment(10)
what i know ipv6 is 64-bit, so servers using ipv6 must be running on a 64-bit operating systems. just use ipv4 for now, there's alot of issues using ipv6 so far.Ezekielezell
@GiantHornet: IPv6 is neither 32-bit nor 64-bit; it can run on either and on other systems too.Cincture
@Ezekielezell yea, I don't think thats right because my ubuntu machine is i686 which is 32-bit and has a IPv6 addressKyanize
@Ezekielezell IPv6 is 128-bit long, but I don't know how OS architecture could be important here. Also, suggesting to drop support for IPv6 is the root of the problems with IPv6.Dragonnade
thanks for addtl info, but so far ipv4 is stable for using sockets.Ezekielezell
@Ezekielezell well if im going to try to learn sockets, IPv4 seems to be pretty easy, i might as well try IPv6 while I'm at itKyanize
so far I'm also doing experiments on IPv6Ezekielezell
@Ezekielezell have you been able to connect to the sockets that you open, or "think" you have opened for IPv6 communication?Kyanize
yes, use asynchronous, you can check this out, msdn.microsoft.com/en-us/library/fx6588te.aspxEzekielezell
@Ezekielezell thanks for the hint but im using C so its not going to help much, I'm using system calls to do it...Kyanize
B
8

Saying "server.sin6_scope_id = 5;" is arbitrary. I fought with this awhile myself and discovered you need to use the actual scope of the actual interface you want to bind on. It can be found with an obsure but useful little function.

#include <net/if.h>
server.sin6_scope_id=if_nametoindex("eth0");

Of course, hardcoding it to one particular adapter is bad, shortsighted coding. A more complete solution is to loop through all of them and match on the ip address you're binding. The following is not perfect in that it doesn't account for quirks like having non-canonical addresses and two adapters with the same ip, etc. But besoverall, this sample function works great and should get you started.

#include <string.h> // strcmp
#include <net/if.h> // if_nametoindex()
#include <ifaddrs.h> // getifaddrs()
#include <netdb.h> // NI_ constants

// returns 0 on error
unsigned getScopeForIp(const char *ip){
    struct ifaddrs *addrs;
    char ipAddress[NI_MAXHOST];
    unsigned scope=0;
    // walk over the list of all interface addresses
    getifaddrs(&addrs);
    for(ifaddrs *addr=addrs;addr;addr=addr->ifa_next){
        if (addr->ifa_addr && addr->ifa_addr->sa_family==AF_INET6){ // only interested in ipv6 ones
            getnameinfo(addr->ifa_addr,sizeof(struct sockaddr_in6),ipAddress,sizeof(ipAddress),NULL,0,NI_NUMERICHOST);
            // result actually contains the interface name, so strip it
            for(int i=0;ipAddress[i];i++){
                if(ipAddress[i]=='%'){
                    ipAddress[i]='\0';
                    break;
                }
            }
            // if the ip matches, convert the interface name to a scope index
            if(strcmp(ipAddress,ip)==0){
                scope=if_nametoindex(addr->ifa_name);
                break;
            }
        }
    }
    freeifaddrs(addrs);
    return scope;
}
Broucek answered 17/4, 2014 at 20:19 Comment(0)
C
6

You're creating a socket in the AF_INET family, but then trying to bind it to an address in the AF_INET6 family. Switch to using AF_INET6 in your call to socket().

Crusado answered 22/11, 2012 at 3:3 Comment(2)
just out of the curiosity you use the telnet fe80::216:3eff:fec3:3c22%eth0 8080 to test it?Kyanize
I passed NULL as the address to that function so that it would bind to in6addr_any. Then I used telnet 0 6666 to connect to it. I'm not able to connect to link local addresses that other ports are listening on either.Crusado

© 2022 - 2024 — McMap. All rights reserved.