What are the requirements for ipv6 multicasting using plain sockets? - c++

I am dealing with an old codebase, in which ipv6 multicast does not seem to work. When I try to bind() the socket to ff01::1, it fails. The socket is being created in my ethernet interface.
Binding the socket to in6addr_any, which is "::", results in a successful bind, but no packet is ever received, except for the ones being send by the application itself, using the given socket (IPV6_MULTICAST_LOOP is set). These packets never seem to leave the application either. They are not visible in wireshark when trying to capture packets in the ethernet interface. Only the incoming outside multicast packets are visible. None of them reach my application.
System is Ubuntu 16.04 with Linux 4.4.0.
A sample of the setup code:
#define MCASTADDRC "ff01::1"
int mcast::bind_mcast(const char *interface) {
this->net = socket(AF_INET6, SOCK_DGRAM, 0);
inet_pton(AF_INET6,MCASTADDRC,&this->multicast.ipv6mr_multiaddr);
this->ifaceaddr.sin6_family = AF_INET6;
this->ifaceaddr.sin6_port = htons(SRVPORT);
this->ifaceaddr.sin6_addr = in6addr_any;
// interface for multicast
this->mcastaddr.sin6_family = AF_INET6;
this->mcastaddr.sin6_port = htons(SRVPORT);
this->mcastaddr.sin6_addr = this->multicast.ipv6mr_multiaddr;
int opcoes = fcntl(this->net, F_GETFL, 0);
if ( fcntl(this->net, F_SETFL, opcoes | O_NONBLOCK) == -1 ) {
// fail
return(false);
}
if (bind(net, (struct sockaddr *) &this->ifaceaddr, sizeof(this->ifaceaddr)) == -1 ) {
// fail
return(false);
}
this->ifaceindex = if_nametoindex(interface);
this->multicast.ipv6mr_interface = this->ifaceindex;
this->ifaceaddr.sin6_scope_id = this->ifaceindex;
int mcast_loop = 1;
if (setsockopt(this->net, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &mcast_loop, sizeof(mcast_loop))) {
//fail
return(false);
}
if (setsockopt(this->net, IPPROTO_IPV6, IPV6_MULTICAST_IF, &this->ifaceindex, sizeof(this->ifaceindex))) {
//fail
return(false);
}
if (setsockopt(this->net, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &this->multicast, sizeof(this->multicast))) {
//fail
return(false);
}
int optval = 6000000;
int optlen = sizeof(optval);
if (setsockopt(this->net, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval))) {
exit(0);
}
if (getsockopt(this->net, SOL_SOCKET, SO_RCVBUF, &optval, (socklen_t *)&optlen)) {
// fail
exit(0);
}
if(optval < 262142) {
// buffer is too small, we failed
exit(0);
}
return(true); // success
}

Bits 12-15 (starting at 0) in an IPv6 multicast address specifies the multicast scope.
A multicast address of the the form ff01::/16 has a scope of 1 which means interface local. Such packets may not be sent over any network link. That's why you can't receive any packets with an address like this from other hosts.
You need to use an address with a different scope value. A scope of 2 may be sent over the local network but not across routers, while a scope of e (15) is globally routeable.
Also, run netstat -ng when your program is running to ensure that you've joined the proper multicast group on the proper interface.
See the Wikipedia page for multicast addresses for more details.

Related

UDP broadcast and multicast messages arrive but recvfrom does not receive anything

I am new to stackoverflow and also pretty much a beginner at programming and hope to find a solution here.
My code is written in C++ and should run on a computer on module with linux operating system. The program should receive messages from other linux or windows systems and then depending on the content of the messages execute further subroutines and send back a response. The windows program is also written in C++. The linux system and the windows system are connected via a switch and the switch is connected to the home network via a powerline adapter. The multicast function is enabled and supported in the switch settings, as well as in the linux system.
The linux code to test the functionality looks like this:
int createIPv4MulticastSocket(uint16_t socket_port, int allowReuseAddress)
{
int Socket;
int broadcast = 1;
sockaddr_in localSock = {};
// Bind to the proper port number with the IP address specified as INADDR_ANY
memset(&localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(socket_port);
localSock.sin_addr.s_addr = INADDR_ANY;
// Creating the Socket
printf("Creating a socket...");
if ((Socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("Creating a socket failed:");
printf("\n");
}
else
{
printf("Socket created. \n");
}
// set the reuse address options
if (setsockopt(Socket, SOL_SOCKET, SO_REUSEADDR, (char*)&allowReuseAddress, sizeof(allowReuseAddress)) < 0)
{
perror("Error setting the reuse address option");
printf("\n");
}
else
printf("Setting the reuse address option...OK. \n");
// bind the socket to the defined address
printf("Try to bind the created Socket to my address. \n");
if (bind(Socket, (struct sockaddr*)&localSock, sizeof(localSock)) == -1) {
perror("Binding socket failed:");
printf("\n");
}
else
{
printf("Bind was succesful. \n");
}
// sets the socket options so you can send Broadcast messages
printf("Setting the socket options to allow Broadcast. \n");
if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &broadcast,
sizeof(broadcast)) == -1) {
perror("Setting the socket options for allowing broadcast failed:");
printf("\n");
}
else
{
printf("Setting the broadcast options...OK. \n");
}
return Socket;
}
void joinMulticastGroup(const char* IPMulticastGroup, const char* IPLocalInterfaceAddr, int SocketDescriptor)
{
struct ip_mreq group;
int LocalIP;
int conv_ip;
if (IPLocalInterfaceAddr[0] == '\0')
{
conv_ip = inet_pton(AF_INET, IPMulticastGroup, &group.imr_multiaddr.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
group.imr_interface.s_addr = htonl(INADDR_ANY);
}
else
{
conv_ip = inet_pton(AF_INET, IPMulticastGroup, &group.imr_multiaddr.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
conv_ip = inet_pton(AF_INET, IPLocalInterfaceAddr, &group.imr_interface.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
}
if (setsockopt(SocketDescriptor, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&group, sizeof(group)) < 0)
{
perror("Adding multicast group error");
printf("\n");
}
else
printf("Adding multicast group...OK. \n");
}
void sendUDP(int sending_socket, const char* destination_ipaddress,
uint16_t destination_port, unsigned char sending_message[], int size_of_sending_message)
{
struct sockaddr_in destination_address;
long int numbytes_send;
int conv_ip;
// define destination address
printf("Convert the destination address to sockaddr_in. \n");
destination_address.sin_family = AF_INET; // IPv4 address
destination_address.sin_port = htons(destination_port); // destination port
conv_ip = inet_pton(AF_INET, destination_ipaddress, &destination_address.sin_addr.s_addr);
if (conv_ip == 0) {
printf("Destination IP-address doesn't contain a valid network address in the specified address family.\n");
}
else if (conv_ip == -1) {
perror("No valid address family:");
printf("\n");
}
memset(destination_address.sin_zero, '\0', sizeof(destination_address.sin_zero)); // fill up sin_zero with "0"
printf("Correct destination address sockaddr. \n");
printf("Sending a message...");
if ((numbytes_send = sendto(sending_socket, sending_message, size_of_sending_message, 0,
(struct sockaddr*)&destination_address, sizeof(destination_address))) == -1) {
perror("sendto() failed:");
printf("\n");
}
else
{
printf(" %i Bytes of data have been sent. \n", numbytes_send);
}
}
void receiveUDP(int receiving_socket, struct sockaddr* received_from,
unsigned char receiving_message[], int size_of_receiving_message)
{
long int numbytes_received;
unsigned int len_received_from = sizeof(*received_from);
socklen_t len_recv_from = len_received_from;
printf("Trying to receive a message...");
if ((numbytes_received = recvfrom(receiving_socket, receiving_message, size_of_receiving_message, 0,
received_from, &len_recv_from)) == -1) {
perror("Receiving message failed:");
printf("\n");
}
else
{
printf("%i Bytes an Daten erhalten\n", numbytes_received);
}
}
int main()
{
struct sockaddr received_from;
int socketfd;
unsigned char sending_message[1472], receiving_message[1472];
const char* destination_ipaddress = "192.168.178.35"; //Laptop
const char* multicast_ipaddress = "224.0.1.14"; //Multicast
const char* broadcast_ipaddress = "192.168.178.255"; //Broadcast
uint16_t destination_port = 3300;
uint16_t port = 3300;
uint16_t messageid = 0;
double altitude = 0;
double longitude = 0;
double lattitude = 0;
// Clean message buffer
memset(sending_message, '\0', sizeof(sending_message));
memset(receiving_message, '\0', sizeof(receiving_message));
// Create a Socket
socketfd = createIPv4MulticastSocket(port, 1);
// join the multicast group
joinMulticastGroup(multicast_ipaddress, "", socketfd);
// Send UDP message.
sendUDP(socketfd, broadcast_ipaddress, destination_port, sending_message, sizeof(sending_message));
/// Receive messages and read Data
while (1)
{
receiveUDP(socketfd, &received_from, receiving_message, sizeof(receiving_message));
messageid = unpackunsignedint16(receiving_message, 1);
altitude = unpackdouble(receiving_message, 3);
lattitude = unpackdouble(receiving_message, 11);
longitude = unpackdouble(receiving_message, 19);
printf("actual altitude is: %lf \n", altitude);
printf("actual lattitude is: %lf \n", lattitude);
printf("actual longitude is: %lf \n", longitude);
}
close(socketfd);
}
If i now send unicast messages to the linux program, everything works as it should and the message is received and the sent values are inserted correct into the printf() functions. Sending unicast messages also works without problems. However, if I want to receive broadcast or multicast messages, the program stops in the recvfrom() line. If I check the connected port with tcpdump port 3300 for incoming messages, they arrive at the linux system. If I try to send broadcast or multicast messages, this does not work and no outgoing messages are displayed in tcpdump.
If I try to receive broadcast or multicast messages and then go back to try again to receive unicast messages, this also does not work anymore. I do not get any error messages during the showed errorchecking.
Thanks in advance for your help.
Edit: Maybe I forgot to mention something because I thought it shouldn't be that big of a deal, but I've also read now that this could be a problem. The application is deployed to the system using a docker container
You're not correctly setting the incoming interface for multicast traffic, and you're not setting the outgoing interface at all.
When you call joinMulticastGroup, you pass an empty string for the second argument which is supposed to contain the IP address of the incoming multicast interface as a string. So if for example the machine's IP is 192.168.178.34, then you pass "192.168.178.34" for that argument.
If you don't set the outgoing multicast interface explicitly, the OS will choose whichever interface is the "default". You should use the IP_MULTICAST_IF socket option, passing the address of a struct in_addr specifying the IP address.

Response doesn't reach socket on multicast in Windows

I'm trying to get a little SSDP client / server up and running. So far the server is working just fine, responding to my M-SEARCH (according to wireshark). The client code is written in Visual Studio using Winsock2 (see code below). The problem is that the response never reaches my recv call when i send the search to the multicast address.
I already tried sending and receiving directly to the server ip address, which will generate a response that reaches my recv call correctly. However, when i change the ip to the multicast addess, it doesn't work (even though i can see the response on Wireshark!). So for some reason the socket (on OS level?) refuses to pass it on to the application.
I should note that the response is always unicast.
Here's my code:
#include <Winsock2.h> // before Windows.h, else Winsock 1 conflict
#include <Ws2tcpip.h> // needed for ip_mreq definition for multicast
#include <Windows.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define SERVERPORT 1900
char buff[] = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: ssdp:discover\r\nST: ssdp:all\r\n\r\n";
int main()
{
char rcvdbuff[1000];
int len, Ret = 2;
WSADATA wsaData;
if (WSAStartup(0x0101, &wsaData)) {
perror("WSAStartup");
return 1;
}
struct sockaddr_in their_addr;
SOCKET sock;
sock = socket(AF_INET, SOCK_DGRAM, 0);
their_addr.sin_family = AF_INET;
////THIS APPROACH DOES NOT WORK
their_addr.sin_addr.s_addr = inet_addr("239.255.255.250");
//THIS APPROACH WORKS - SOMEHOW THE SOCKET IS BOUND TO THIS IP AND CAN THUS RECEIVE
//their_addr.sin_addr.s_addr = inet_addr("192.168.3.90");
their_addr.sin_port = htons(SERVERPORT);
len = sizeof(struct sockaddr_in);
while (1)
{
printf("buff:\n%s\n", buff);
Ret = sendto(sock, buff, strlen(buff), 0, (struct sockaddr*)&their_addr, len);
if (Ret < 0)
{
printf("error in SENDTO() function");
closesocket(sock);
return 0;
}
//Receiving Text from server
printf("\n\nwaiting to recv:\n");
memset(rcvdbuff, 0, sizeof(rcvdbuff));
Ret = recvfrom(sock, rcvdbuff, sizeof(rcvdbuff), 0, (struct sockaddr *)&their_addr, &len);
if (Ret < 0)
{
printf("Error in Receiving");
return 0;
}
rcvdbuff[Ret - 1] = '\0';
printf("RECEIVED MESSAGE FROM SERVER\t: %s\n", rcvdbuff);
//Delay for testing purpose
Sleep(3 * 1000);
}
closesocket(sock);
WSACleanup();
}
I tried one interesting thing (without restarting the application!):
1) First send to the direct ip address (192.168.3.90)
2) Get response
3) Now send to the multicast address
4) Now the response gets back just fine!
It's as if the socket somehow 'knows' the unicast address from the first send/recv call.
Does anyone know what to do or how to debug?
I think I've found the solution to the question: Windows Firewall.
Here's a quote over from Quora:
Only connections that are explicitly allowed, using firewall rules, are permitted. Windows Firewall, by default, allows all outboundconnections, and permits only established inbound connections (that is, an inbound connection that is in direct response to an outbound connection initiated from your computer or network).
This is exactly the situation: We've not established an outbound connection, and thus it's blocked by Windows Firewall!
In the other case, when I first send directly, Windows Firewall opens up for that exact inbound connection and therefore the subsequent multicast send gets a response.

Win Socket UDP connection-less recvfrom() Error

I'm working on a legacy VC6 application, that uses winsocket to listen to a UDP port for incoming packets. However I am getting the following errors. If I use WSAGetLastError() I get WSAECONNRESET, which if I read the description, doesn't seem to make sense, because its saying that remote host forcibly closed the socket, But I want use UDP connection-less manor, so it shouldn't matter what the other machine is doing... we should just listen. If I check the errno and use sterror() I get the following message. "No such file or directory". (I think this it's enum is EIO, according to http://pubs.opengroup.org/onlinepubs/009695399/functions/recvfrom.html)
I've had some success in narrowing down the issue, it appears if I take out a sendto() call, that calls back on the same port as the recvfrom(), the code seems to work ok. So something with that sendto() is putting in a bad state.
I'm looking for suggestions on why this socket is going bad, and either how to prevent or recover.
Edit
Here is the other weird part, if do the setup again for that socket (after a recvfrom() fails)... it all seems to work, even additional calls to sendto() don't seem trigger a recvfrom() fail, which in turn would call the setup again..
CODE
static VOID SetupSocketAddress( SOCKADDR_U &saRx, int nRTDPort )
{
memset(&saRx.saIPX, 0, sizeof(SOCKADDR_IPX));
saRx.saIPX.sa_family = AF_IPX; // IPX type address
memset(saRx.saIPX.sa_netnum,0x00,4); // we may have to get this number
memset(saRx.saIPX.sa_nodenum,0xff,6); // broadcast address
saRx.saIPX.sa_socket=(unsigned short)nRTDPort; // socket number
}
void CRealTimeData::SetupSocket( CRealTimeData * lpRTD, BOOL &bDone, SOCKADDR_U &saRx, int nRTDPort, SOCKADDR_U &saFrom, int &cbAddr,
DWORD &dwLocalAddress, int &nMaxIpIpxBuf, char * &pbyIpIpxRxBuf, int nFlags, BOOL bDo)
{
char szErrorCode[32];
int nReturn = 0;
if (lpRTD->m_eSourceType == V7_RTD_IPX)
{
// open IPX socket
// packet type = 4
lpRTD->m_Socket=socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX+4);
if (lpRTD->m_Socket == INVALID_SOCKET)
{
nReturn = AddSocketErrorToEventViewer( lpRTD);
bDone = TRUE;
}
// Socket must be bound prior to calling recvfrom()
// setup address
SetupSocketAddress(saRx, nRTDPort);
#ifdef _DEBUG_IPX
// test changing host number to network number
// we can't actually change though, because other programs use it this way
u_short nNetPort=0;
int nRet=WSAHtons(lpRTD->m_Socket, (unsigned short)nRTDPort, &nNetPort);
TRACE(_T("RTDIpxThread: Host Port=%04x Net Port=%04x RTD Input=%d \n"),nRTDPort, nNetPort, lpRTD->GetInputNumber());
#endif
// setup address for Sending Data on RTD
SetupSocketAddress( lpRTD->m_saRTD, nRTDPort );
// copy address - Why are we copying the address just over right later (in recvfrom() )? -NG
memcpy(&saFrom.saIPX, &lpRTD->m_saRTD.saIPX, sizeof(SOCKADDR_IPX));
cbAddr = sizeof(SOCKADDR_IPX);
}
else
{
// open IP socket
lpRTD->m_Socket=socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); // ??? should this use IPPROTO_UDP???
if (lpRTD->m_Socket == INVALID_SOCKET)
{
nReturn = AddSocketErrorToEventViewer( lpRTD);
bDone = TRUE;
}
// Socket must be bound prior to calling recvfrom()
// setup address
memset(&saRx.saIP, 0, sizeof(SOCKADDR_IN));
saRx.saIP.sin_family = AF_INET; // IP type address
saRx.saIP.sin_port=htons((u_short)nRTDPort); // PORT number
saRx.saIP.sin_addr.s_addr=htonl(INADDR_ANY); // ADDRESS number
// setup for Sending Data on RTD port
memset(&lpRTD->m_saRTD.saIP, 0, sizeof(SOCKADDR_IN));
lpRTD->m_saRTD.saIP.sin_family = AF_INET; // IP type address
lpRTD->m_saRTD.saIP.sin_port=htons((u_short)nRTDPort); // PORT number
lpRTD->m_saRTD.saIP.sin_addr.s_addr=htonl(INADDR_BROADCAST); // ADDRESS number
// copy address - Why are we copying the address just over right later (in recvfrom() )? -NG
memcpy(&saFrom.saIP, &lpRTD->m_saRTD.saIP, sizeof(SOCKADDR_IN));
cbAddr = sizeof(SOCKADDR_IN);
char szHostName[MAX_PATH+1];
if (gethostname(szHostName, MAX_PATH)==0)
{
hostent *phe=gethostbyname(szHostName);
dwLocalAddress = *(DWORD*)&phe->h_addr_list[0];
}
} // end IP socket
if (!bDone)
{
// enable broadcasting
BOOL bOptVal=TRUE;
nReturn=setsockopt(lpRTD->m_Socket, SOL_SOCKET, SO_BROADCAST, (char *)&bOptVal, sizeof(BOOL));
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
}
// enable reuse of address
bOptVal=TRUE;
nReturn=setsockopt(lpRTD->m_Socket, SOL_SOCKET, SO_REUSEADDR, (char *)&bOptVal, sizeof(BOOL));
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
}
// get the socket's max message size
int nOptSize=sizeof(UINT);
UINT nMaxMsgSize=600;
nReturn=getsockopt(lpRTD->m_Socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&nMaxMsgSize, &nOptSize);
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
nMaxMsgSize=600; // default max size
}
nMaxIpIpxBuf=nMaxMsgSize; // always create buffer that is as big as the sockets max message size
pbyIpIpxRxBuf = new char[nMaxIpIpxBuf]; // allocate buffer for receiving data from socket
if (!pbyIpIpxRxBuf)
bDone = TRUE;
else
memset(pbyIpIpxRxBuf,0,nMaxIpIpxBuf*sizeof(char));
// bind to address
nReturn=bind(lpRTD->m_Socket, &saRx.sa, cbAddr);
if (nReturn == SOCKET_ERROR)
{
nReturn = AddSocketErrorToEventViewer(lpRTD);
bDone = TRUE;
}
// send data to indicate startup
if (lpRTD->m_eProtocol == V7_RTD_ENHANCED)
{
int nLen=lpRTD->BuildErrorMsg(V7_ERTD_SERVICE_STARTUP, szErrorCode, sizeof(szErrorCode));
nReturn=sendto(lpRTD->m_Socket,szErrorCode,nLen, nFlags, &lpRTD->m_saRTD.sa, cbAddr);
if (nReturn == SOCKET_ERROR)
{
nReturn=WSAGetLastError();
}
}
} // end if not done
}
Main()
nFromLen = cbAddr;
nReturn=recvfrom(lpRTD->m_Socket, pbyIpIpxRxBuf, nMaxIpIpxBuf, nFlags, &saFrom.sa, &nFromLen);
if(nReturn == SOCKET_ERROR)
{
SetupSocket(lpRTD, bDone, saRx, nRTDPort, saFrom, cbAddr, dwLocalAddress, nMaxIpIpxBuf, pbyIpIpxRxBuf,nFlags, FALSE);
nReturn=recvfrom(lpRTD->m_Socket, pbyIpIpxRxBuf, nMaxIpIpxBuf, nFlags, &saFrom.sa, &nFromLen);
}
// if i take this out no error....
nReturn=sendto(lpRTD->m_Socket, szErrorCode, nLen, nFlags, &saFrom.sa, cbAddr);

how to specify which interface to use for a socket in linux

Is it possible to bind a udp socket to a specific interface so it sends data through that interface? I have an application that uses several Udp Sockets to send data and it is running on a machine with several interfaces. I know it's possible to do this by specifying the interface name by using this code:
int UdpSocket::open(const char *interface)
{
send_fd_ = ::socket(AF_INET, SOCK_DGRAM, 0);
if (send_fd_ < 0)
{
perror("socket");
return -1;
}
int val = 1;
int rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
if (rc < 0)
{
perror("sesockopt");
close();
return -1;
}
unsigned char ttl = 16;
rc = ::setsockopt(send_fd_, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
if (rc < 0)
{
perror("sesockopt_ttl");
close();
return -1;
}
if (interface != NULL)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), interface);
rc = ::setsockopt(send_fd_, SOL_SOCKET, SO_BINDTODEVICE, (void*)&ifr, sizeof(ifr));
if (rc < 0)
{
perror("sesockopt");
close();
return -1;
}
}
const int flags = ::fcntl(send_fd_, F_GETFL, 0);
::fcntl(send_fd_, F_SETFL, flags | O_NONBLOCK);
return 0;
}
But this requires that the app is run with root privileges, otherwise it it will throw an error saying the "operation not permitted."
The easiest, and by far the most sane, approach is to add route(s) matching your multicast destinations:
~# route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0
since OS network stack selects outbound interface for multicast packets based on the routing table. This also works for listening - you just bind to group address and kernel would pick correct interface for you. You still have to join the group as usual.
From the manpage:
SO_BINDTODEVICE
Bind this socket to a particular device like “eth0”, as specified in
the passed interface name. If the name is an empty string or the
option length is zero, the
socket device binding is removed. The passed option is a variable-length null-terminated interface name string with the
maximum size of IFNAMSIZ. If a socket
is bound to an interface, only packets received from that particular interface are processed by the socket. Note that this
only works for some socket types,
particularly AF_INET sockets. It is not supported for packet sockets (use normal bind(2) there).
This means you have to get the interface from the name yourself, possibly using getifaddrs, and then bind to that address.

Problem in listening to multicast in C++ with multiple NICs

Hi
I am trying to write a multicast client on a machine with two NICs, and I can't make it work.
I can see with a sniffer that once I start the program the NIC (eth4) start receiving the multicast datagrams, However, I can't recieve() any in my program.
when running "tshark -i eth4 -R udp.port==xxx (multicast port)"
I get:
1059.435483 y.y.y.y. (some ip) -> z.z.z.z (multicast ip, not my eth4 NIC IP) UDP Source port: kkk (some other port) Destination port: xxx (multicast port)
Searched the web for some examples/explanations, but it seems like I do what everybody else does.
Any help will be appreciated...
Thanks!
bool connectionManager::sendMulticastJoinRequest()
{
struct sockaddr_in localSock;
struct ip_mreqn group;
char* mc_addr_str = SystemManager::Instance()->getTCP_IP_CHT();
char* local_addr_str = SystemManager::Instance()->getlocal_IP_TOLA();
int port = SystemManager::Instance()->getTCP_Port_CHT();
/* Create a datagram socket on which to receive. */
CHT_UDP_Feed_sock = socket(AF_INET, SOCK_DGRAM, 0);
if(CHT_UDP_Feed_sock < 0)
{
perror("Opening datagram socket error");
return false;
}
/* application to receive copies of the multicast datagrams. */
{
int reuse = 1;
if(setsockopt(CHT_UDP_Feed_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0)
{
perror("Setting SO_REUSEADDR error");
close(CHT_UDP_Feed_sock);
return false;
}
}
/* Bind to the proper port number with the IP address */
/* specified as INADDR_ANY. */
memset((char *) &localSock, 0, sizeof(localSock));
localSock.sin_family = AF_INET;
localSock.sin_port = htons(port);
localSock.sin_addr.s_addr =inet_addr(local_addr_str); // htonl(INADDR_ANY); //
if(bind(CHT_UDP_Feed_sock, (struct sockaddr*)&localSock, sizeof(localSock)))
{
perror("Binding datagram socket error");
close(CHT_UDP_Feed_sock);
return false;
}
/* Join the multicast group mc_addr_str on the local local_addr_str */
/* interface. Note that this IP_ADD_MEMBERSHIP option must be */
/* called for each local interface over which the multicast */
/* datagrams are to be received. */
group.imr_ifindex = if_nametoindex("eth4");
if (setsockopt(CHT_UDP_Feed_sock, SOL_SOCKET, SO_BINDTODEVICE, "eth4", 5) < 0)
return false;
group.imr_multiaddr.s_addr = inet_addr(mc_addr_str);
group.imr_address.s_addr = htonl(INADDR_ANY); //also tried inet_addr(local_addr_str); instead
if(setsockopt(CHT_UDP_Feed_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&group, sizeof(group)) < 0)
{
perror("Adding multicast group error");
close(CHT_UDP_Feed_sock);
return false;
}
// Read from the socket.
char databuf[1024];
int datalen = sizeof(databuf);
if(read(CHT_UDP_Feed_sock, databuf, datalen) < 0)
{
perror("Reading datagram message error");
close(CHT_UDP_Feed_sock);
return false;
}
else
{
printf("Reading datagram message...OK.\n");
printf("The message from multicast server is: \"%s\"\n", databuf);
}
return true;
}
Before the call to IP_ADD_MEMBERSHIP set up group like this:
group.imr_multiaddr.s_addr = inet_addr(mc_addr_str);
group.imr_address.s_addr = inet_addr(local_addr_str);
You can leave the call to SO_BINDTODEVICE in before that, but that's only necessary if you would only like to use one specific interface.
I set up the group exactly like that (code is updated).
I also add to the routing table "route add -net 224.0.0.0 netmask 240.0.0.0 dev eth4"
still, with no success.
anything else I need to set up in my environment besides that?