Search code examples
c++linuxnetwork-programmingipv6multicast

What are the requirements for ipv6 multicasting using plain sockets?


I am dealing with an old codebase, in which ipv6 multicast does not seem to work. When I try to bind() the socket to ff01::1, it fails. The socket is being created in my ethernet interface.

Binding the socket to in6addr_any, which is "::", results in a successful bind, but no packet is ever received, except for the ones being send by the application itself, using the given socket (IPV6_MULTICAST_LOOP is set). These packets never seem to leave the application either. They are not visible in wireshark when trying to capture packets in the ethernet interface. Only the incoming outside multicast packets are visible. None of them reach my application.

System is Ubuntu 16.04 with Linux 4.4.0.

A sample of the setup code:

    #define MCASTADDRC "ff01::1"

    int mcast::bind_mcast(const char *interface) {

    this->net = socket(AF_INET6, SOCK_DGRAM, 0);        
    inet_pton(AF_INET6,MCASTADDRC,&this->multicast.ipv6mr_multiaddr); 

    this->ifaceaddr.sin6_family = AF_INET6;
    this->ifaceaddr.sin6_port = htons(SRVPORT);
    this->ifaceaddr.sin6_addr = in6addr_any;

    // interface for multicast
    this->mcastaddr.sin6_family = AF_INET6;
    this->mcastaddr.sin6_port = htons(SRVPORT);
    this->mcastaddr.sin6_addr = this->multicast.ipv6mr_multiaddr;


    int opcoes = fcntl(this->net, F_GETFL, 0);

    if ( fcntl(this->net, F_SETFL, opcoes | O_NONBLOCK) == -1 ) {
        // fail
        return(false);
    }

    if (bind(net, (struct sockaddr *) &this->ifaceaddr, sizeof(this->ifaceaddr)) == -1 ) {
        // fail                    
        return(false);
    }

    this->ifaceindex = if_nametoindex(interface);
    this->multicast.ipv6mr_interface = this->ifaceindex;
    this->ifaceaddr.sin6_scope_id = this->ifaceindex;

    int mcast_loop = 1;
    if (setsockopt(this->net, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &mcast_loop, sizeof(mcast_loop))) {
        //fail
        return(false);
    }

    if (setsockopt(this->net, IPPROTO_IPV6, IPV6_MULTICAST_IF, &this->ifaceindex, sizeof(this->ifaceindex))) {
        //fail
        return(false);
    }

    if (setsockopt(this->net, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &this->multicast, sizeof(this->multicast))) {
        //fail
        return(false);
    }
    int optval = 6000000;
    int optlen = sizeof(optval);
    if (setsockopt(this->net, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval))) {

        exit(0);
    }
    if (getsockopt(this->net, SOL_SOCKET, SO_RCVBUF, &optval, (socklen_t *)&optlen)) {
        // fail
        exit(0);
    }

    if(optval < 262142) {   
        // buffer is too small, we failed 
        exit(0);
    }

    return(true); // success

}

Solution

  • Bits 12-15 (starting at 0) in an IPv6 multicast address specifies the multicast scope.

    A multicast address of the the form ff01::/16 has a scope of 1 which means interface local. Such packets may not be sent over any network link. That's why you can't receive any packets with an address like this from other hosts.

    You need to use an address with a different scope value. A scope of 2 may be sent over the local network but not across routers, while a scope of e (15) is globally routeable.

    Also, run netstat -ng when your program is running to ensure that you've joined the proper multicast group on the proper interface.

    See the Wikipedia page for multicast addresses for more details.