Receiving multicast on linux host with multiple interfaces - c++

I have a host running Ubuntu 16.04 connected to one network via the primary wired network interface, and to another network via a USB-to-Ethernet adapter. Using tcpdump, I am able to verify incoming multicast packets on both network interfaces. However, my application does not receive any multicast data from the secondary interface. If I disconnect the cable to the primary interface, and then restart my application, then I do receive the data from the secondary interface. It is only with both interfaces connected that the application does not receive from the secondary interface.

I found a similar problem (Raspberry Pi Zero with USB-to-Ethernet adaptor, fails to respond to mDNS queries). To work out if your problem is the same, does does your app correctly receive multicast traffic whilst running tcpdump at the same time? And does running tcpdump with --no-promiscuous-mode not see multicast traffic?
If your answer is yes to both, then the workaround I've found is simply ip link set eth0 promisc on. I don't know if it is a hardware bug (I'm using a Kontron DM9601 adaptor, ID 0FE6:9700) or a driver bug, but either way, enabling promiscuous mode seems to fix multicast reception for me. Alternatively you could try a better USB-to-ethernet adaptor.

The ip_mreq stucture is passed as the option value for the IP_ADD_MEMBERSHIP socket option to join a multicast group. From the Multicast programming HOWTO from the The Linux Documentation Project:
The first member, imr_multiaddr, holds the group address you want to join. Remember that memberships are also associated with interfaces, not just groups. This is the reason you have to provide a value for the second member: imr_interface. This way, if you are in a multihomed host, you can join the same group in several interfaces. You can always fill this last member with the wildcard address (INADDR_ANY) and then the kernel will deal with the task of choosing the interface.
The IP_MULTICAT_IF socket option is also relevant on a multihomed host to set the outbound interface for multicast data sent via the socket. More information on these socket options, the ip_mreq structure, and the newer ip_mreqn stucture is found here.
For those using Boost on a multihomed host, you will need to use the native handle to join the group on specific interfaces. As of Boost 1.58 running on Ubuntu 16.04, the socket option abstraction ip::multiast::join_group() joins the group on an interface of the kernel's choosing and does not allow the developer to specify an interface. The socket option abstraction ip::multicast::outbound_interface() controls the outbound interface but does not affect which interface the socket receives on.
Here is a code sample to join a group on a specific interface based on the local interface IP address:
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr(discovery_ip);
mreq.imr_interface.s_addr = inet_addr(local_interface_ip);
if(setsockopt(socket_.native_handle(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) {
... handle error ...
}

Related

Manually specify which network interface to send data

I have implemented a service which sends and receives data over a tcp socket. Now there are two network interfaces on my machine, which I'm supposed to run the service over two networks.
How can I specify which network interface to send data? (Using Boost::asio or traditional Berkeley sockets, or by using a system call to change kernel's routing table on the fly)
I don't know about Boost, but in general if you want to send/recv using a specific interface than you need to bind() the socket to that interface's IP address, or alternatively on some platforms you can use setsockopt(SOL_SOCKET, SO_BINDTODEVICE) instead.
Did you tried this, create a socket bind to 0.0.0.0 for receiving packet from all interfaces. Then create one socket for each interface for sending packet. Key thing here is to set REUSEADDRSS option so that socket for sending can bind to address per-interface and sharing the same port of the one receiving packet.
If you are dealing with TCP, an established connection will ready bind to a specific interface. You won't be able to send to that connection by other interfaces.

How to determine sender's mulicast group&port pair from the received multicast message

I have written a service (C++) that runs on the AIX machine and receives Multicast messages from a remote host (my service joins a predefined Multicast group&port pair).
Lately, I've been asked to handle the Multicast messages received from a different host (on a different Multicast group&port, on the same socket. Though I can insist on the same port.
The issue is, that the messages received from each host must be handled differently.
How do I distinguish between these messages? Is there a possibility to retrieve the sender's Multicast group&port pair from the socket/received messages?
While you can identify the remote host's address (not multi-cast group!) with recvfrom(); it's generally not a good idea to perform different actions for different host with listening same port. It's against the rule of least surprise.
So the short answer is to differentiate remote host address.
If you want to differentiate multi-cast groups on the same port, you have to bind multiple sockets with the multi-cast group address.
The code looks like this:
addr.set(239,0,0,1,8888);
udp.socket();
udp.joinmcast(&addr);
udp.bind(&addr);

How to find the destination address of a UDP packet using boost::asio?

I'm developing a peer-to-peer communications network for use over a LAN in an industrial environment. Some messages are are just asynchronous, and don't require a response. Others are request-response. The request messages (and the async messages) are sent to a multicast group, and the replies to requests are sent unicast. Each endpoint, therefore, receives UDP packets that are sent to the multicast group, and also receives messages that are just sent to it using plain unicast.
So far it's working fine, but there doesn't seem to be any way in boost::asio to find out the destination address of a received UDP packet (using socket.async_receive_from) - whether it was sent to the multicast group or the actual interface. I can use the contents of the message to infer whether it was sent multicast or unicast, but it would be nice to be able to also check the destination address.
We are currently using Windows 7, but will be transitioning to Linux in the future.
Is there a way to find the destination address of a UDP packet received using boost::asio?
Unfortunately this is not possible with boost::asio, and usually is not "the way to do" it, as you try to access Transport Layer information at the Application Layer.
So you basically have two options:
a) Write non-portable system code with for example IP_PKTINFO or SO_BINDTODEVICE on Linux. Example Code can be found on the boost asio mailing list here
b) use two distinct sockets, one for the multicast and one for the unicast. You therefore need to specify a listen_address other than "0.0.0.0" on each socket.
udp::endpoint(address_v4::from_string("239.192.152.143"), 6771)
This Question on SO might also be helpful: Using a specific network interface for a socket in windows

C++ windows32 winsock UDP routing?

In C++ using Windows32 using windows socket library using UDP is there a way to give a client routing information to another client to establish a connection between clients without having to route through the server
Clarification:
server - waits for computers and gives routing info - a detached server
client - sends a ack request and waits for routing info - a normal user computer
but ok so its not posible to give routing info to clients to interconnect clients without requiring the data to be forwarded through the server?
Short answer: no.
Long answer: No matter what information you include in your UDP packet, at the transport layer it's just another IP packet, and your NIC will slap the appropriate headers on it and send it on its way. Unless the hosts are directly connected to each other, the network topology will dictate how many hops (routers/switches) it has to make to get there.
Addendum:
I'm not sure what you mean by server (I read it as "router" initially, but you could just as easily have been talking about a Domain Name Server (DNS)). If you are trying to avoid DNS lookup, you can easily do this by providing an IP address directly (assuming you know it). However, DNS lookup is a one-time process--once the IP address is known, the DNS host is not involved in routing your UDP packets in any way.
Short answer: no
Long answer: yes --- but you have to use IPPROTO_IP, not IPPROTO_UDP. Use IP_OPTIONS option in setsockopt() to set source routing.

Windows Peer to Peer Global_ Group without third party ipv6 tunnel

I have been trying to develop a peer to peer application that uses Micosoft's Peer to Peer Group library. Basing my work on the Creating a Group Chat Application acrticle on msdn. This works fine for local groups and will also work for global groups if I have a thrid party tunnel adapter installed such as the gogo6 client. However from a few things I have read it seems like I should be able to get things working through the Teredo tunnel adapter that comes built into Windows.
I have tried various things and can now access ipv6 only sites (eg ipv6.google.com) without the gogo6 tunnel running, but I can't seem to find any other peers in my global group through this method.
I have added a rule allowing trafic (including edge traversal) for the application in the Windows Firewall and also opened the following ports to incoming and outgoing trafic.
tcp 3587
udp 3540, 1900
From the samples I have read it seems like it should just work, but it doesn't. I did read that to use teredo in an application you had to specificaly enable it. The only way I have found to do this is when opening the socket, but the group api does all of that for you so I have no known way of controlling that.
Some Teredo clients are unreachable due to symmetric router problem. Teredo can work only behind 90% of routers. Gogo6 uses TSP which tunnels the packet to gogo6 infrastructure from where it reaches ipv6 internet.
I don't think Teredo supports IPv6 multicast. If the Peer to Peer Group library uses multicast under the hood, I think that's the problem. I could never find any confirmation that multicast is unsupported by Teredo; but in my own testing setsockopt(ADD_GROUP_MEMBERSHIP) would always fail when the interface ID was a Teredo interface.