Binding Sockets to IPv6 Addresses

34,873

Solution 1

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;
}

Solution 2

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().

Share:
34,873
tpar44
Author by

tpar44

Updated on August 16, 2020

Comments

  • tpar44
    tpar44 over 3 years

    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.
    
  • tpar44
    tpar44 over 11 years
    just out of the curiosity you use the telnet fe80::216:3eff:fec3:3c22%eth0 8080 to test it?
  • qqx
    qqx over 11 years
    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.