Receiving different multicast with same source address on different interfaces - c

On a system with several network interfaces, I can have the same multicast address and port combination used on different networks, with different data on them. I want to be able to connect to them with several network cards and receive different data on each interface.
To do so, I bind to the interface I want to receive on using the IP_MULTICAST_IF option:
ip_mreqn mreqn;
memset(&mreqn, 0, sizeof (ip_mreqn));
mreqn.imr_multiaddr.s_addr = inet_addr(mc);
mreqn.imr_address.s_addr = INADDR_ANY;
mreqn.imr_ifindex = if_nametoindex(device);
if (setsockopt(mct->fd, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn)) < 0) {
perror("setsockopt multicast if");
return 1;
}
and make sure the join request is only sent on that interface by setting IP_ADD_MEMBERSHIP with the same structure:
if (setsockopt(mct->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0) {
perror("setsockopt add membership");
return 1;
}
While the IP_ADD_MEMBERSHIP code works (the join request is only sent on the interface specified), the IP_MULTICAST_IF does not. Instead once it is able to join the multicast on any of the interfaces, I receive the same data through all sockets, even if they have different imr_ifindex set.

The IP_MULTICAST_IF ioctl does not control incoming selection, it is setting the socket's default interface for its outgoing multicast packets.
IP_ADD_MEMBERSHIP is the only mechanism for configuring incoming multicast and the memberships it creates are for the entire host, the host does not tailor delivery to individual sockets based on their requested memberships. (You can observe the hosts memberships with netstat -gn and the reference count is being used to determine when the host can stop observing, but not which sockets are receiving fan-out. If you have a matching membership from any socket, all sockets that made an applicable bind(2) will start receiving that multicast, even if they have never used IP_ADD_MEMBERSHIP.)
The usual method to differentiate these packets with out changing the system setup, is to receive them all on a socket using ancillary data to identify their interface. On Linux, this ancillary data setup is done with IP_PKTINFO as described in ip(7).

Related

How to use lwip_fcntl OR lwip_ioctl function to make the socket non-blocking for lwip_accept() function?

As this is my first question, so my apologies if I couldn't ask in a proper way.
I am implementing the server-client communication using lwip.
My server must be connected to multiple clients all the time.
For that I made my server listening on all its ports, and then when client wants to connect, the client will send the connect request and the server should accept it.
But the problem I am having is that the server keeps on waiting on lwip_accept() function for each client, which blocks the other clients connections also.
For example:
if there are 3 clients, who want to connect to server. Server is listening on its 3 ports for example. and now the lwip_accept() functions are being called like below:
client1Conn = lwip_accept(sock1, (struct sockaddr*)&client, (socklen_t*)&lenClient);
client2Conn = lwip_accept(sock2, (struct sockaddr*)&client, (socklen_t*)&lenClient);
client2Conn = lwip_accept(sock3, (struct sockaddr*)&client, (socklen_t*)&lenClient);
Now for some reason, if the client 1 is not present, then the server will not move further from the first lwip_accept call, as long as the client 1 becomes available, and till that time the other two clients who are available, will not be able to connect to server.
What I want is that the server checks for the client connection, and if the client is not available, then it should skip that and move to the next client connection.
What I tried is using lwip_fcntl like below
int flags = lwip_fcntl(sock1, F_GETFL, 0);
lwip_fcntl(sock1, F_SETFL, flags | O_NONBLOCK);
client1Conn = lwip_accept(sock1, (struct sockaddr*)&client, (socklen_t*)&lenClient);
But no effect, and lwip_ioctl like below
int nonblocking = 1;
lwip_ioctl(sock1, FIONBIO, &nonblocking);
client1Conn = lwip_accept(sock1, (struct sockaddr*)&client, (socklen_t*)&lenClient);
This makes the accept failed all the time.
I tried the solution given in How do I change a TCP socket to be non-blocking? but it didn't work either.
Is there any possible way to implement the required functionality?

Multiple outgoing ipv6 addresses

I need to implement the following scheme (in C, however the language is not the case here):
client(192.168.1.2) <-> proxy(addr4: 192.168.1.1:1000, addr6: FE80:0000:0000:0000:0202:B3FF:FE1E:8329) <-> some_remote_host(remote_addr6)
The thing is addr6 on proxy side must be dynamically changed according to incoming ipv4 port. For example:
client connects to 192.168.1.1:1000, outgoing connection is made via addr6_0
client connects to 192.168.1.1:1001, outgoing connection is made via addr6_1
etc ...
The most straight-forward implementation would be: assign multiple static ipv6 addrs to ethernet interface and use bind() on socket before outgoing connection. The problem is that the number of incoming ports/outgoing addrs can be ~10000 (and as far as I understood recommended value for net.ipv6.conf.all.max_addresses is 32 or 64, default is 16).
The questions are:
What possible problems can I expect if I assign 10000 ipv6 addrs to one interface, I assume performance issues?
Is there a better way to achieve the goal?

connected udp multicast needs bind in the sender side

I have an ambiguous issue, I have a multicast group between two users one of them is a sender and another is receiver I did these scenarios in each side:
Receiver:
create a udp socket.
bind to a multicast group address
connect to the sender side ( connect(sender ip))
join the multicast group
recv from the multicast group.
Sender:
create a udp socket.
send to the multicast group.
In this scenario above when sender sent data receiver couldn't receive but if we checked the receiver side by tcpdump there is data was received from multicast group.
but if there is no connect to sender side in receiver, data will be received.
BUT Actually, If we let the sender bind to the multicast address before send to the multicast group and also receiver connect to the sender side as scenario above data will be received successfully!!!!
Any explanation when we added the bind in the sender side ???
You might want to connect(2) the sender's socket to the multicast group to speed up sending, but don't connect(2) the receiver since it restricts it to unicast (yes, it's a bit confusing, but that's how it works). Just bind(2) the receiver to the group/port, and do the setsockopt(2) with IP_ADD_MEMBERSHIP to join the group.
There is no such thing as 'connected UDP multicast'. It is is connected or it is multicast. Remove the connect() steps completely.
On the receiver side, both the bind() and connect() calls do the same thing: they associate a given Internet socket address with a given connectionless socket. For the bind() call -- in which the Internet socket address is the address of the multicast group -- this means that the socket will only receive UDP packets whose destination address is that of the multicast group. For the connect() call -- in which the Internet socket address is that of the sender -- this means that the socket will only receive UDP packets whose destination address is that of the sender, which isn't what you want.
The connect() call is overriding the bind() call, resulting in no packets being received.
Replace the bind() call with a connect() call to the multicast group and you should still receive the UDP packets -- or keep only the bind() call. It's your call.

Refresh multicast group membership

I have several embedded machines listening and streaming rtp audio data to a multicast group. They are connected to a smart managed switch (Netgear GS108Ev2) which does basic igmp snooping and multicast filtering on its ports, so that the rest of my (W)LAN doesn't get flooded.
At start everything works fine for about 500-520 seconds. After that, they don't receive any more data until they leave and join the group again. I guess the switch is "forgetting" about the join after a timeout.
Is there any way to refresh the group membership, i.e. letting the switch know, that there ist still someone listening, without losing packets?
System info:
Arch: blackfin
# cat /proc/version
Linux version 2.6.28.10-ADI-2009R1-uCBF54x-EMM
(gcc version 4.3.3 (ADI) ) #158 PREEMPT Tue Jun 5 20:05:42 CEST 2012
This is the way multicast / the IGMP protocol works. A client has to join the group periodically by sending a Membership Report or it will be assumed that he has left the group after some short timeout. However, those reports are usually sent only when receiving a Membership Query from the local multicast router. Either your clients don't receive the query or don't respond with a report.
Try to use a tool like wireshark in order to see which IGMP packets are sent through your network.
You need an IGMP querier to send the Membership Queries, as was already explained by scai.
If you can't configure your router to do that, you can use one of your computers. Seeing how running a full multicast routing daemon would be overkill (and I've never done that), I suggest you try to abuse igmpproxy.
First create a dummy upstream interface (this is not persistent!):
ip tap add dev tap6 mode tap
Write igmpproxy.conf:
# Dummy upstream interface.
phyint tap6 upstream ratelimit 0 threshold 1
# Local interface.
phyint eth0 downstream ratelimit 0 threshold 1
# Explicitly disable any other interfaces (yes, it sucks).
phyint NAME disabled
...
Finally start igmpproxy (as root):
igmpproxy -v /path/to/igmpproxy.conf
If your embedded devices are running linux, you need to turn off the reverse packet filter on them or they won't respond to group membership queries. In that case the upstream switch will assume there is no-one listening to that multicast and switch it off.
I had same problem, multicast on wifi was lost after 260 seconds, I solved it with my application by adding AddSourceMembership on socket.
private void StartListner(IPAddress sourceIp, IPAddress multicastGroupIp, IPAddress localIp, int port)
{
try
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint localEndpoint = new IPEndPoint(localIp, port);
socket.Bind(localEndpoint);
byte[] membershipAddresses = new byte[12]; // 3 IPs * 4 bytes (IPv4)
Buffer.BlockCopy(multicastGroupIp.GetAddressBytes(), 0, membershipAddresses, 0, 4);
Buffer.BlockCopy(sourceIp.GetAddressBytes(), 0, membershipAddresses, 4, 4);
Buffer.BlockCopy(localIp.GetAddressBytes(), 0, membershipAddresses, 8, 4);
socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddSourceMembership, membershipAddresses);
try
{
byte[] b = new byte[1024 * 2];
int length = socket.Receive(b);
}
catch { }
}
catch (Exception ex)
{
logger.Error("Exception: " + ex);
}
}

Subscribing to multiple multicast groups on one socket (Linux, C)

Is it possible to receive data from more than one multicast group on a single socket?
For example:
void AddGroup(int sock,
const char* mc_addr_str,
int mc_port,
const char* interface) {
struct sockaddr_in mc_addr;
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr(mc_addr_str);
mc_addr.sin_port = htons(mc_port);
if ((bind(sock, (struct sockaddr *) &mc_addr,
sizeof(mc_addr))) < 0) {
perror("bind() failed");
exit(1);
}
// construct an IGMP join request structure
struct ip_mreq mc_req;
mc_req.imr_multiaddr.s_addr = inet_addr(mc_addr_str);
mc_req.imr_interface.s_addr = inet_addr(interface);
if ((setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(void*) &mc_req, sizeof(mc_req))) < 0) {
perror("setsockopt() failed");
exit(1);
}
}
This code works when I add one multicast group. But when I try to add another, the "bind" fails. I don't quite understand why the bind needs to be there in the first place? (but the code doesn't work without it).
Ideally I would like to call AddGroup multiple times on the same socket. Is this possible? Or do I need one socket per group and then just use polling?
You can join as many multicast groups as you like, using the appropriate setsockopt() call with the IP_ADD_MEMBERSHIP option, rather than bind().
You only bind a socket once. Skip the bind the second time and see what happens.
You can join as many multicast groups you want to on a single socket. See setsockopt(), IP_PKTINFO for a way to recognize which multicast group you are reading data from.
bind to the passive address, i.e. 0.0.0.0 for IPv4 and use ASM or SSM to pull in additional groups, e.g. IP_ADD_MEMBERSHIP as listed.
You can only bind once.
Yes, it's possible: look on the example in the link (http://www.tenouk.com/Module41c.html)
To shorten this up in a few steps:
You setsockopt with SO_REUSEADDR
You bind on INADDR_ANY
You setsockopt with IP_ADD_MEMBERSHIP on every group you want to receive datagram from.
It seems to me that using IP_PKTINFO gives an option to distinguish received packets, but sender must take care about preparing them(Setting the source IP for a UDP socket)
In unix based OSes:
If you need to bind to multicast address, you cannot call bind() more than once. And you will need to bind to multicast address when you expect more than one multicast streams using same destination port and multiple processes running in same device receiving those multicasts.
For example, when you have multicast streams: 239.0.0.1:1234, 239.0.0.2:1234, 239.0.0.3:1234 and 239.0.0.4:1234, and you want to receive 239.0.0.1, 239.0.0.2 in process-A and want to receive 239.0.0.3, 239.0.0.4 in process-B, you cannot accomplish this when both processes A and B running in same device.

Resources