recvfrom works with INADDR_ANY, but specifying certain interface doesn't work

1.6k Views Asked by At

I wrote a program that join source specific multicast group and receive udp multicast packets:

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

typedef unsigned int        UINT32;

int join_ssm_group(int s, UINT32 group, UINT32 source, UINT32 inter) {
    struct ip_mreq_source imr; 
    imr.imr_multiaddr.s_addr  = group;
    imr.imr_sourceaddr.s_addr = source;
    imr.imr_interface.s_addr  = inter;
    return setsockopt(s, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *) &imr, sizeof(imr));  
}

UINT32 LISTEN_INTERFACE = inet_addr("10.10.1.2");

int main(int argc, char *argv[]) {

    if (argc<3) {
        printf(" Use: %s <group> <source> <port>", argv[0]);
        return 1;
    }

    //  Make socket 
    int sd = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);

    struct sockaddr_in Sender;
    socklen_t SenderAddrSize = sizeof( Sender );

    struct sockaddr_in binda;

    //  Bind it to listen appropriate UDP port
    binda.sin_family = AF_INET;
    binda.sin_port = htons( atoi(argv[3]));
      = INADDR_ANY;
    // binda.sin_addr.s_addr = LISTEN_INTERFACE;
    bind(sd,(struct sockaddr*)&binda, sizeof(binda));

    // Join to group
    join_ssm_group( sd, inet_addr(argv[1]), 
        inet_addr(argv[2]), 
        INADDR_ANY );

    char        buf[65536];
    UINT32      seq;

    while(1) {
        printf("try receive\n");
        int res=recvfrom(sd,(char*)buf,sizeof(buf),0, (struct sockaddr *)& Sender, &SenderAddrSize);
        printf("received\n");
        seq = *(UINT32*)buf;
        printf("scr=:%12s;\tseq=%6d;\tlen=%4d\n", inet_ntoa(Sender.sin_addr), seq, res);
    }

    return 0;
}

It works fine but note that I'm using binda.sin_addr.s_addr = INADDR_ANY;. netstat shows this:

netstat -a | grep 16002
udp        0      0 0.0.0.0:16002           0.0.0.0:*

When I change it to binda.sin_addr.s_addr = LISTEN_INTERFACE; program stops working - it can not recieve packets, it hangs in recvfrom. netstat shows this:

netstat -a | grep 16002
udp        0      0 localhost.localdo:16002 0.0.0.0:*

In both cases tcpdump shows that data is online, so the problem is that I can not receive data on the specific interface, only on ALL interfaces. I'm using RHEL 7, teaming, and LISTEN_INTERFACE is the IP of the corresponding VLAN. Why my code doesn't work and how to troubleshoot it? I do not want to use INADDR_ANY for performance reasons - listening ALL interfaces would be more expensive than listeining certain interface.

upd passing LISTEN_INTERFACE to both join_ssm_group and and binda.sin_addr.s_addr doesn't work too. BTW similar Windows version of such code works on the same PC under Windows Server 2008 R2, but it doesn't work in RHEL 7. I guess I should check these:

  • if RHEL 7 receives data on the requreid interface on the required port (answer is Yes, proved by tcpdump)
  • if socket is listening on the required interface on the required port (answer is Yes, proved by netstat?)
  • if both answers above are Yes then how is it possible that call to recvfrom doesn't receive data?

Well probably this question more about RHEL 7 now, than about c++.

1

There are 1 best solutions below

7
On

When you join the multicast group you need to specify the same interface that you are listening on, or join it via all interfaces in a loop.

However listening on all interfaces is the norm. It is not 'slow', and it is a 'good idea', unless you have a specific reason to restrict who can connect.