Brad Walker
Brad Walker

Reputation: 51

Disable self-reception of UDP multicasts

I am having a real bear of time trying to understand IP multicasting. In particular, I want to disable packets from being looped back into the multicast group.

I just threw this code together to example what I am seeing.

/*

example.c

 */

#include <sys/types.h>

#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <pthread.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#define EXAMPLE_PORT 6000
#define EXAMPLE_GROUP "224.0.0.1"

const long MYSENDER = 0; // send thread ID
const long MYRECVR = 1; // recv thread ID

int status;
int sock;

void *sender(void *threadid);
void *receiver(void *threadid);

main(int argc) {
    pthread_t threads[2];
    struct sockaddr_in addr;

    /* Set up socket */
    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sock == -1) {
        perror("socket");
        exit(1);
    }

    status = pthread_create(&threads[MYRECVR], NULL, receiver, (void *) MYRECVR); // Start the server thread and check for error
    if (status) {
        fprintf(stderr, "Error: pthread_create(receiver) returned %d\n", status);
        exit(-1);
    }

    status = pthread_create(&threads[MYSENDER], NULL, sender, (void *) MYSENDER); // Start the client thread and check for error
    if (status) {
        fprintf(stderr, "Error: pthread_create(sender) returned %d\n", status);
        exit(-1);
    }

    pthread_join(threads[MYRECVR], NULL); // main() will wait for the server thread to complete
    pthread_join(threads[MYSENDER], NULL); // main() will wait for the client thread to complete
}

void *sender(void *threadid) {
    int c;
    char message[50];
    struct sockaddr_in addr;
    int addrlen, cnt;

    memset((char *) &addr, 0, sizeof (addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_addr.s_addr = inet_addr(EXAMPLE_GROUP);
    addr.sin_port = htons(EXAMPLE_PORT);
    addrlen = sizeof (addr);

    fprintf(stderr, "Starting sender thread!\n");

    /* Send */
    while (1) {
        time_t t = time(0);
        sprintf(message, "time is %-24.24s", ctime(&t));
        printf("sending: %s\n", message);
        cnt = sendto(sock, message, sizeof (message), 0,
                (struct sockaddr *) &addr, addrlen);
        if (cnt < 0) {
            perror("sendto");
            exit(1);
        }
        sleep(5);
    }
    return 0;
}

void *receiver(void *threadid) {
    int opt = 1;
    char message[50];
    struct sockaddr_in addr;
    int addrlen, cnt;
    struct ip_mreq mreq;

    fprintf(stderr, "Starting receiver thread!\n");

    memset((char *) &addr, 0, sizeof (addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(EXAMPLE_PORT);
    addrlen = sizeof (addr);


    /* Receive */

    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt));
    if (bind(sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
        perror("bind");
        exit(1);
    }

    mreq.imr_multiaddr.s_addr = inet_addr(EXAMPLE_GROUP);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
            &mreq, sizeof (mreq)) < 0) {
        perror("setsockopt mreq");
        exit(1);
    }

    while (1) {
        cnt = recvfrom(sock, message, sizeof (message), 0,
                (struct sockaddr *) &addr, &addrlen);
        if (cnt < 0) {
            perror("recvfrom");
            exit(1);
        } else if (cnt == 0) {
            break;
        }
        printf("%s: message = \"%s\"\n", inet_ntoa(addr.sin_addr), message);
    }
    return 0;
}

It runs fine and produces the following output...

picozed@ubuntu:/tmp$ ./test
Starting receiver thread!
Starting sender thread!
sending: time is Wed Aug 16 19:23:29 2017
10.249.27.51: message = "time is Wed Aug 16 19:23:29 2017"
sending: time is Wed Aug 16 19:23:34 2017
10.249.27.51: message = "time is Wed Aug 16 19:23:34 2017"
^C
picozed@ubuntu:/tmp$

But, I would like to prevent the messages from being looped back in...

Upvotes: 1

Views: 3135

Answers (1)

dbush
dbush

Reputation: 224522

By default, most OS's will loop outgoing multicast traffic back to the local machine. If you want to disable this behavior, you need to set the IP_MULTICAST_LOOP socket option to 0.

int opt=0;
if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, &opt, sizeof (opt)) < 0) {
    perror("setsockopt");
    exit(1);
}

Upvotes: 2

Related Questions