2

I have broadly the following multicast socket code. It works fine. Now I need to join two multicast channels on the same machine like 224.10.13.18 - 55001 224.10.13.34 - 55001

and depending on which ip address it came from, I need to treat the message differently.

The question is to how to use create two sockets for multicast channels where the port values are same, such that each socket only returns read on data that is sent to that channel.

  /* create socket to join multicast group on */
  socket_file_descriptor_ = socket ( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
  if ( socket_file_descriptor_ < 0 ) 
    { // fprintf ( stderr, "cannot open socket \n");
      exit(1);
    }

  /* set reuse port to on to allow multiple binds per host */ 
  {
    int flag_on = 1;
    if ( ( setsockopt ( socket_file_descriptor_, SOL_SOCKET, SO_REUSEADDR, &flag_on,
                        sizeof(flag_on) ) ) < 0 ) 
      { // fprintf ( stderr, "MulticastReceiverSocket setsockopt() SOL_SOCKET SO_REUSEADDR failed\n");
        exit(1);
      }
  } 

  struct ip_mreq mc_req;
  inet_pton ( AF_INET, listen_ip_.c_str(), &(mc_req.imr_multiaddr.s_addr) ); 

  mc_req.imr_interface.s_addr = htonl(INADDR_ANY);

  /* send an ADD MEMBERSHIP message via setsockopt */
  if ( ( setsockopt ( socket_file_descriptor_, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
                      (void*) &mc_req, sizeof(mc_req))) < 0) 
  { // std::cerr << "setsockopt() failed in IP_ADD_MEMBERSHIP " << listen_ip_ << ": "<< listen_port_ << std::endl;
      exit(1);
  } 


  /* construct a multicast address structure */
  struct sockaddr_in mcast_Addr;
  bzero ( &mcast_Addr, sizeof(mcast_Addr) );
  mcast_Addr.sin_family = AF_INET;
  mcast_Addr.sin_addr.s_addr = htonl(INADDR_ANY);
  mcast_Addr.sin_port = htons ( listen_port_ );
  /* bind to specified port onany interface */
  if ( bind ( socket_file_descriptor_, (struct sockaddr *) &mcast_Addr, sizeof ( struct sockaddr_in ) ) < 0 ) 
  { // fprintf ( stderr, "%s cannot bind %s:%d \n", "MulticastReceiverSocket", listen_ip_.c_str(), listen_port_ ) ;
     exit(1);
  } 
Humble Debugger
  • 4,439
  • 11
  • 39
  • 56
  • And the question is? There is no question here, and you haven't provided an error message to indicate that the code you posted doesn't work, but try doing the bind before the add-membership. – user207421 Dec 19 '13 at 21:46
  • I don't know if I understood correctly: 1. You want to listen to two different machines on one machine on one port and act depending what machine connects to you, or 2. You want to listen on two different Ip addresses (2 different network cards I suppose) and act depending on who connect to what machine – fernando.reyes Dec 19 '13 at 22:05
  • 1
    @fernando.reyes You didn't. It is neither. He wants to listen to multicasts, which rules out (1), and he is using INADDR_ANY, which rules out (2). You don't appear to know what multicast is actually, which rules out any possibility that you can answer the question. – user207421 Dec 19 '13 at 22:10
  • Data of both channels are accessible through the same interface. If the ports were different I could have created two sockets using above code and each would return data only on that channel. But this breaks down if the channels have the same port value. I am trying to figure out how to handle overlapping port values. – Humble Debugger Dec 22 '13 at 12:03
  • @EJP I did the best job I could of editing the question to provide context of my exact problem. I believe it is a fairly general problem and a solution should be useful for others. I am new to drafting code heavy questions. Help appreciated. – Humble Debugger Dec 22 '13 at 12:05
  • Perhaps SO_REUSEPORT is useful? – brm Dec 25 '13 at 13:22

1 Answers1

1

You only need one socket for this. If you set the IP_PKTINFO option on a call to setsockopt, you can use recvmsg to get a struct in_pktinfo, which will contain the destination IP address. Then you can choose how to process the packet based on that.

Borrowing from https://stackoverflow.com/a/5309155/1687119 (error checking removed for brevity):

// 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
}
Community
  • 1
  • 1
dbush
  • 205,898
  • 23
  • 218
  • 273