mab0189
mab0189

Reputation: 136

Error setsockopt IP_ADD_MEMBERSHIP: No such device

I writting an application that should be able to receive IPv4 or IPv6 multicast datagrams on a socket. I wrote a function that enables the reception of multicast datagrams for a socket through setsockopt (see code below). The strange problem that i am having is that the setsockopt for the IPv4 case IP_ADD_MEMBERSHIP is sometimes failing with the errno No such device and the other times it is working as expected. My application is running on a raspberry pi with raspbian. I really appreciate your suggestions!

void setRecvMulticastAddr(int *socketFD, struct sockaddr *addr, char *interface, char *multicastaddr){

    // Cast the sockaddr to a sockaddr storage struct
    struct sockaddr_storage *addrStorage = (struct sockaddr_storage *) addr;

    // Check if it is an IPv4 or IPv6 socket
    if(addrStorage->ss_family == AF_INET){

        // IPv4 multicast request
        struct ip_mreq mreq;

        // Convert the multicast IPv4 address string to an in_addr
        struct in_addr multiaddr;
        if(inet_pton(AF_INET, multicastaddr, &multiaddr) != 1){
            printf("Could not convert the IPv4 multicast address: %s", multicastaddr);
            exit(ERR_INETPTON_FAILED);
        }

        // Cast the sockaddr to a sockaddr_in struct
        struct sockaddr_in *addrin = (struct sockaddr_in *) addrStorage;

        // Fill out the IPv4 multicast request
        mreq.imr_interface = addrin->sin_addr;
        mreq.imr_multiaddr = multiaddr;

        // ### The setsockopt that fails sometimes: ###
        // Set the sockopt so the socket can receive on the multicast address
        if(setsockopt(*socketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0){
            perror("Error setsockopt IP_ADD_MEMBERSHIP failed");
            exit(ERR_SETSOCKOPT_FAILED);
        }

    }else{

        // IPv6 multicast request
        struct ipv6_mreq mreq;

        // Set the interace name in the ifr
        struct ifreq ifr;
        snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface);

        // Get the ifrindex based on the interface name
        if(ioctl(*socketFD, SIOGIFINDEX, &ifr) < 0){
            printf("Could not get ifrindex: %s\n", strerror(errno));
        }

        // Convert the multicast addrress string to an in_addr6
        struct in6_addr multiaddr;
        if(inet_pton(AF_INET6, multicastaddr, &multiaddr) != 1){
            printf("Could not convert the IPv6 multicast address: %s", multicastaddr);
            exit(ERR_INETPTON_FAILED);
       }

       // Fill out the IPv6 multicast request
       mreq.ipv6mr_interface = ifr.ifr_ifindex;
       mreq.ipv6mr_multiaddr = multiaddr;

       // Set the sockopt so the socket can receive on the multicast address
       if(setsockopt(*socketFD, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) != 0){
            perror("Error setsockopt IPV6_JOIN_GROUP failed");
            exit(ERR_SETSOCKOPT_FAILED);
       }

    }

    return;
}

Upvotes: 1

Views: 2209

Answers (1)

mab0189
mab0189

Reputation: 136

Based on the discussion with @dbush i hopefully improved the code by:

  1. Using the new recommended struct ip_mreqn instead of the old struct ip_mreq
  2. Determining the address of the interface with ioctl rather than using the sockaddr

Here is the new code for the IPv4 case:

    // IPv4 multicast request
    struct ip_mreqn mreqn;

    // Set the interace family and name in the ifr
    struct ifreq ifr;
    ifr.ifr_addr.sa_family = AF_INET;
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface);

    // Get the ifrindex of the interface
    if(ioctl(*socketFD, SIOGIFINDEX, &ifr) < 0){
        printf("Error could not get the interface index: %s\n", strerror(errno));
        close(*socketFD);
        exit(ERR_IOCTL_INDEX);
    }

    mreqn.imr_ifindex = ifr.ifr_ifindex;

    // Get the IP address of the interface
    if(ioctl(*socketFD, SIOCGIFADDR, &ifr) != 0){
        printf("Error could not get the interface address: %s\n", strerror(errno));
        close(*socketFD);
        exit(ERR_IOCTL_ADDR);
    }

    // Cast the sockaddr to a sockaddr_in to get the in_addr value
    struct sockaddr_in *ifaddr = (struct sockaddr_in*) &ifr.ifr_addr;
    mreqn.imr_address = ifaddr->sin_addr;

    // Convert the multicast IPv4 address string to an in_addr
    struct in_addr multiaddr;
    if(inet_pton(AF_INET, multicastaddr, &multiaddr) != 1){
        printf("Error could not convert the IPv4 multicast address: %s", multicastaddr);
        exit(ERR_INETPTON_FAILED);
    }

    mreqn.imr_multiaddr = multiaddr;

    // Set the sockopt so the socket can receive on the multicast address
    if(setsockopt(*socketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn)) != 0){
        perror("Error setsockopt IP_ADD_MEMBERSHIP failed");
        exit(ERR_SETSOCKOPT_FAILED);
    }

Upvotes: 1

Related Questions