0
votes

I have a client joining two different multicast groups(same port number) on the same machine. On the client side, I am using epoll to listen on both the sockets. The server tries to send a multicast msg to first group. However, epoll receives the data on both the sockets. Is it because sockets are on the same machine and using the same port ? Please advice

Code snippet :

/* Client code to join multicast group */

multicastPort = "4321";                                                                                                                                                          
  /* Resolve the multicast group address */                                                                                                                                      
  hints.ai_family = PF_UNSPEC;                                                                                                                                                   
  hints.ai_flags  = AI_NUMERICHOST;                                                                                                                                              
  if ((status = getaddrinfo(group_ip_address, NULL, &hints, &multicastAddr)) != 0)                                                                                               
    {                                                                                                                                                                            
        perror("\nError g.");                                                                                                                                                    
    }                                                                                                                                                                            

  hints.ai_family   = multicastAddr->ai_family;                                                                                                                                  
  hints.ai_socktype = SOCK_DGRAM;                                                                                                                                                
  hints.ai_flags    = AI_PASSIVE; /* Return an address we can bind to */                                                                                                         
  if ( getaddrinfo(NULL, multicastPort, &hints, &localAddr) != 0 )                                                                                                               
        perror("\nError f.");                                                                                                                                                    

/* Create socket for receiving datagrams */                                                                                                                                      
  if ( (sd = socket(localAddr->ai_family, localAddr->ai_socktype, 0)) < 0 )                                                                                                      
    perror("socket() failed");                                                                                                                                                   

  /* lose the pesky "Address already in use" error message */                                                                                                                    
  if (setsockopt(sd,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(int)) == -1)                                                                                                      
    perror("setsockopt");                                                                                                                                                        

  /* Bind to the multicast port */                                                                                                                                               
  if ( bind(sd, localAddr->ai_addr, localAddr->ai_addrlen) != 0 )                                                                                                                
    perror("bind() failed");                                                                                                                                                     

  struct ip_mreq multicastRequest;  /* Multicast address join structure */                                                                                                       

      /* Specify the multicast group */                                                                                                                                          
      memcpy(&multicastRequest.imr_multiaddr,                                                                                                                                    
       &((struct sockaddr_in*)(multicastAddr->ai_addr))->sin_addr,                                                                                                               
       sizeof(multicastRequest.imr_multiaddr));                                                                                                                                  

      /* Accept multicast from any interface */                                                                                                                                  
      multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);                                                                                                                 

      /* Join the multicast address */                                                                                                                                           
      if ( setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &multicastRequest, sizeof(multicastRequest)) != 0 )                                                             
  perror("setsockopt() failed");                                                                                                                                                 
  /* Create a datagram socket on which to receive. */  
==================================================

/* client code to listen on epoll  sockets*/
int fd_id= multicast_join(lo,group_ip);                                                                                                                                       
   //sprintf(display,"Listening to group %s ip address %s\n", grp_name, grp_ip_address);                                                                                         
   sprintf(display,"Listening to group %s and ip %s\n", grp_name, grp_ip_address);                                                                                               
   PRINT(display);                                                                                                                                                               
   if(fd_id > 0){                                                                                                                                                                
     ADD_CLIENT_IN_LL(client_info,grp_name,group_ip,fd_id);                                                                                                                      
     event->data.fd = fd_id;                                                                                                                                                     
     char buf[30];                                                                                                                                                               
     sprintf(buf,"fd_id %d",fd_id);                                                                                                                                              
     PRINT(buf);                                                                                                                                                                 
     event->events = EPOLLIN|EPOLLET;                                                                                                                                            

     status = epoll_ctl(efd, EPOLL_CTL_ADD, fd_id, event);                                                                                                                       

     if ( status == -1)                                                                                                                                                          
     {                                                                                                                                                                           
       perror("\nError while adding FD to epoll event.");                                                                                                                        
       exit(0);                                                                                                                                                                  
     } 
1
I think it is. You need to use a single socket and the IP_PKTINFO option to determine which group each incoming datagram was sent to. - user207421

1 Answers

1
votes

When you have two UDP sockets open on the same IP and port, any multicast packets that arrive will be received by both sockets. If a unicast packet arrives, whether one or the other or both receive the packet is implementation defined.

If you want to know what the destination IP address is for an incoming packet, you need to set the IP_PKTINFO socket option and use recvmsg instead of recvfrom to get this additional data.

// sock is bound AF_INET socket, usually SOCK_DGRAM
// include struct in_pktinfo in the message "ancilliary" control data
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
// the control data is dumped here
char cmbuf[0x100];
// the remote/source sockaddr is put here
struct sockaddr_in peeraddr;
// if you want access to the data you need to init the msg_iovec fields
struct msghdr mh = {
    .msg_name = &peeraddr,
    .msg_namelen = sizeof(peeraddr),
    .msg_control = cmbuf,
    .msg_controllen = sizeof(cmbuf),
};
recvmsg(sock, &mh, 0);
for ( // iterate through all the control headers
    struct cmsghdr *cmsg = CMSG_FIRSTHDR(&mh);
    cmsg != NULL;
    cmsg = CMSG_NXTHDR(&mh, cmsg))
{
    // ignore the control headers that don't match what we want
    if (cmsg->cmsg_level != IPPROTO_IP ||
        cmsg->cmsg_type != IP_PKTINFO)
    {
        continue;
    }
    struct in_pktinfo *pi = CMSG_DATA(cmsg);
    // at this point, peeraddr is the source sockaddr
    // pi->ipi_spec_dst is the destination in_addr
    // pi->ipi_addr is the receiving interface in_addr
}