Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to choose the correct port for multicast in linux / C

I have tried to do a multicast on a local network in C. My main problem is that:- When I am trying to execute multiple instance listening to the broadcaster, only one client receives the message and all of the others are not receiving anything.

Multicaster Code(the one who send the create the multicast group):

int create_multidiffusion(int port, struct sockaddr_in6 *adr6)
{
    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        perror("Socket creation error");
        return -1;
    }
    memset(adr6, 0, sizeof(*adr6));
    (*(adr6)).sin6_family = AF_INET6;
    (*(adr6)).sin6_port = htons(port);
    (*(adr6)).sin6_addr = in6addr_any;
    int index = 0;

    if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) < 0)
    { // For multidiffusion
        perror("Erreur initialisation interface locale");
        close(sock);
        return -1;
    }
    index = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &index, sizeof(index)) < 0)
    {
        perror("Error Reussadr");
        close(sock);
        return -1;
    }

    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &index, sizeof(index)) < 0)
    {
        perror("Error Reussadr");
        close(sock);
        return -1;
    }

    if (bind(sock, (struct sockaddr *)adr6, sizeof(*adr6)) < 0)
    { // Bind
        perror("Error bind");
        close(sock);
        return -1;
    }

    struct ipv6_mreq group;
    inet_pton(AF_INET6, adresse, &group.ipv6mr_multiaddr);
    group.ipv6mr_interface = ifindex;
    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &group, sizeof(group)) < 0)
    { 
        perror("erroi joining multicast group");
        close(sock);
        return -1;
    }

    if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &index, sizeof(index)) < 0)
    {
        perror("Error activation multicast loop");
        close(sock);
        return -1;
    }

    while(1){
        char *buf = readline(NULL);
        size_t s = sendto(sock, buf, strlen(buf), 0, (struct sockaddr *)adr6, sizeof(*adr6));
        printf("I have send %ld data \n", s);
        free(buf);
    }
    exit(1);
    // return sock;
}

Client Code:

int join_multidiffusion(char *adresse_multidiffusion, int port, struct sockaddr_in6 *adr6)
{
    int sock = socket(AF_INET6, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        perror("Erreur creation socket");
        return -1;
    }
    memset(adr6, 0, sizeof(*adr6));
    (*(adr6)).sin6_family = AF_INET6;
    (*(adr6)).sin6_addr = in6addr_any;
    (*(adr6)).sin6_port = htons(port);

    int index = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &index, sizeof(index)) < 0)
    {
        perror("Error Reussadr");
        close(sock);
        return -1;
    }
    index = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &index, sizeof(index)) < 0)
    {
        perror("Error Reussport");
        close(sock);
        return -1;
    }

    if (bind(sock, (struct sockaddr *)adr6, sizeof(*adr6)) < 0)
    {
        perror("Bind error");
        close(sock);
        return -1;
    }

    struct ipv6_mreq group;
    group.ipv6mr_interface = ifindex;
    if (inet_pton(AF_INET6, adresse_multidiffusion, &group.ipv6mr_multiaddr) <= 0)
    {
        perror("Error inet");
        close(sock);
        return -1;
    }

    if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &group, sizeof(group)) < 0)
    { // Join the multicast group
        perror("error joining multicast group");
        close(sock);
        return -1;
    }
    char buf[1000];
    memset(buf,'\0',1000);
    while(1){
        recv(sock, buf, 1000, 0);
        printf("I have received : %s\n", buf);
    }
    exit(0);

My goal is to make it work, on my PC first, then on LAN. I first tried using the same IP address for both the subscriber and the broadcaster, but with different port numbers — that didn’t work. Then I tried using the same IP address and the same port number, which worked at first. However, when I sent a message from the broadcaster, only one subscriber in the multicast group received it, while the others got nothing. My goal is for all subscribers in the multicast group to receive the message.

The input I have tried for the ipv6 address ff12::1234 , ff12::1:2:3 , the port : 5555,7777 and the value of ifindex was either 0 for the automatic interfaces or 2 which was corresponding to the interfaces of my PC wlo1. My OS is UBUNTU, if that makes any difference.

like image 384
user24537176 Avatar asked Oct 15 '25 16:10

user24537176


2 Answers

I first tried using the same IP address for both the subscriber and the broadcaster, but with different port numbers — that didn’t work.

No, it wouldn't.

Then I tried using the same IP address and the same port number, which worked at first.

Yes, it could. And the difference between this case and the previous one should be a clue to you.

However, when I sent a message from the broadcaster, only one subscriber in the multicast group received it, while the others got nothing.

Sort of. You clarified in comments that

It's running on a single machine with several receiver instances, but only one instance receives the messages that are sent, while the others receive nothing.

In that case, you need to understand that in IP-based multicasting, multicast group membership is based on IP address alone, not port. (And in MAC-based multicasting it is based on the network interface's MAC alone.) If broadcaster and receivers are running on the same, single-homed machine then your multicast group has only one member (to the extent that you are in control of it). All of those one members are receiving your multicast messages.

Moreover, although it is not relevant to multicast group membership, port number has its normal significance for sending and receiving multicast datagrams. The sender specifies a destination port number, and each group member (can) receive the datagram at that specific port. You demonstrated that for yourself already, with the first two tests you described. You might still be seeing that in your multi-receiver test.

But even if all your receivers were bound to the same port, recv()ing a datagram ordinarily consumes it, so that it cannot be received again by that or any other process. You could avoid that by using the MSG_PEEK flag, but then you have the significant problem of somehow ensuring that each receiver process receives each multicast datagram exactly once, and that each one is consumed as promptly as possible so that the next one can be received. A mechanism for that could be built, but it would be a lot of hassle if its only purpose is to support a test configuration that you don't expect to be representative of normal usage of the system.

On the other hand, if multiple receivers running on the same machine is a usage mode you want to fully support, then you've got significant additional work cut out for you. But before you set out on that journey, do be mindful that at least on Linux, all sockets bound to a given address / port pair must at least be associated with processes having the same effective UID. That and analogous restrictions on other platforms are going to limit the use cases you could support even if you go ahead with this.

like image 139
John Bollinger Avatar answered Oct 18 '25 19:10

John Bollinger


The problem here is you're not sending where you think you're sending to.

You first set adr6 to address/port [::]:1234 which you bind to. You then pass adr6 to sendto as the destination.

This does two things. First, it means you only ever send to the same port you're bound to, so the receiver port has to match the sender port. Second, you're sending to IP [::] which defaults to the unicast address of the first adapter. And because you're sending to a unicast address instead of a multicast address, only one of the receivers bound to that port will receive the packet.

You need to populate a separate instance of a sockaddr_in6 for the destination, and pass that to sendto.

struct sockaddr_in6 dest;
dest.sin6_family = AF_INET6;
dest.sin6_port = htons(destport);
inet_pton(AF_INET6, adresse, &dest.sin6_addr);
while(1){
    char buf[100];
    fgets(buf, sizeof buf, stdin);
    size_t s = sendto(sock, buf, strlen(buf), 0, (struct sockaddr *)&dest, sizeof(dest));
    printf("I have send %ld data \n", s);
}

Once you do this, all receivers will receive the packet, and the sender doesn't need to use the same port as the receivers (unless it also wants to receive the same data) nor does it need to join the multicast group.

Also, not that using SO_REUSEADDR and SO_REUSEPORT won't be necessary if you only run one instance of the receiver on a given host. It only matters now while you're testing multiple clients on one host.

like image 38
dbush Avatar answered Oct 18 '25 21:10

dbush



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!