zeromq / libzmq

ZeroMQ core engine in C++, implements ZMTP/3.1
https://www.zeromq.org
Mozilla Public License 2.0
9.79k stars 2.36k forks source link

Dish socket should bind on multicast group IP instead of INADDR_ANY #4582

Open NoAnyLove opened 1 year ago

NoAnyLove commented 1 year ago

Issue description

For dish socket with UDP multicast, if it binds on INADDR_ANY (see code here), it will receive messages from other multicast IPs on the same port.

Given an example, we have 4 apps,

Note: D1 and D2 are on the same box

We expect D1 only receives messages from R1, and D2 only from R2. But in fact, D1 will receive messages from both R1 and R2, so does D2.

This prevents us to segregate the traffic with the same port, as mentioned in issue lcm-proj/lcm#186.

Can we change the bind to the multicast IP, which prevents us from receiving messages from other multicast addresses?

Quote from UNIX Network Programming, Volume 1,

Recall that we could just bind the wildcard IP address and port 8888, but binding the multicast address prevents the socket from receiving any other datagrams that might arrive destined for port 8888.

I'm more than happy to submit a PR if you agree with this change.

Reference:

Environment

Minimal test code / Steps to reproduce the issue

N/A

ljluestc commented 1 month ago

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

#define MULTICAST_GROUP_1 "224.1.1.1"
#define MULTICAST_GROUP_2 "224.1.1.2"
#define PORT 9000
#define BUFSIZE 1024

void receive_multicast(const char* multicast_group) {
    int sock;
    struct sockaddr_in local_addr;
    char buffer[BUFSIZE];
    int n;

    // Create a socket
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // Set up the local address structure
    memset(&local_addr, 0, sizeof(local_addr));
    local_addr.sin_family = AF_INET;
    local_addr.sin_port = htons(PORT);
    // Bind to the specific multicast address
    inet_pton(AF_INET, multicast_group, &local_addr.sin_addr);

    // Bind the socket
    if (bind(sock, (struct sockaddr*)&local_addr, sizeof(local_addr)) < 0) {
        perror("bind");
        close(sock);
        exit(EXIT_FAILURE);
    }

    // Join the multicast group
    struct ip_mreq mreq;
    inet_pton(AF_INET, multicast_group, &mreq.imr_multiaddr);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY); // Local interface
    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt");
        close(sock);
        exit(EXIT_FAILURE);
    }

    printf("Receiving messages on multicast group: %s\n", multicast_group);

    while (1) {
        n = recv(sock, buffer, BUFSIZE, 0);
        if (n < 0) {
            perror("recv");
            close(sock);
            exit(EXIT_FAILURE);
        }
        buffer[n] = '\0'; // Null-terminate the received data
        printf("Received: %s\n", buffer);
    }

    close(sock);
}

int main(int argc, char *argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <multicast_group>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    receive_multicast(argv[1]);
    return 0;
}