Search code examples
cnetwork-programmingudpmulticast

Can I bind ip and port to receive mulitcast traffic from a specific network interface


On Linux, I want to receive multicast traffic only from a specific interface. So I bind the interface and port to socket, then join the multicast group by setsockopt with IP_ADD_MEMBERSHIP option. Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define PORT 60000
#define MULTICAST_IP "239.1.0.1"
#define BUFFER_SIZE 1024

int main(int argc, char const *argv[]) {
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;
    struct ip_mreq mreq;
    char buffer[BUFFER_SIZE];

    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    memset(&servaddr, 0, sizeof(servaddr));
    memset(&cliaddr, 0, sizeof(cliaddr));

    servaddr.sin_family = AF_INET;
    //servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_addr.s_addr = inet_addr("192.168.1.11");
    servaddr.sin_port = htons(PORT);

    if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP);
    mreq.imr_interface.s_addr = inet_addr("192.168.1.11");

    if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    socklen_t len = sizeof(cliaddr);
    char addr[17] = {0};
    int n = recvfrom(sockfd, (char *)buffer, BUFFER_SIZE, MSG_WAITALL, (struct sockaddr *)&cliaddr, &len);
    buffer[n] = '\0';
    inet_ntop(AF_INET, &cliaddr.sin_addr, addr, static_cast<socklen_t>(sizeof(addr)));
    printf("Multicast message from %s received: %s\n", addr, buffer);


    close(sockfd);
    return 0;
}

I can't receive any multicast traffic of group 239.1.0.1 that is from 192.168.1.11. If I bind INADDR_ANY instead of 192.168.1.11, it works as expected.

From this answer, it seems that I can only bind INADDR_ANY and filter the multicast traffic by setsockopt with IP_ADD_MEMBERSHIP option. Is it correct?

Thanks.


Solution

  • On Linux you have two options for receiving multicast traffic.

    1. Bind the socket to INADDR_ANY.
    2. Bind the socket to the desired multicast address.

    Then set the IP_ADD_MEMBERSHIP option on that socket to listen for the given multicast address on the given interface.

    In the first case, you'll also receive any unicast traffic sent to the bound port on any interface. In the latter case, you'll only receive the multicast traffic.

    If you bind to a specific interface, then use the IP_ADD_MEMBERSHIP you won't get any multicast traffic. Note that this behavior is different from Windows, where you can still bind to a specific interface and receive multicast traffic.