How to receive multicast UDP packets correctly? - c++

new to multicast networking, I need to receive UDP packets from a multicast channel through one of the NICs on my windows box, followed Microsoft docs and some blog entry, but still having issues.
I create a socket via
ls = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
call.
Then setsockopt to SO_REUSEADDR
unsigned int reuse = 1;
if( setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) < 0 )
{
LOG4CXX_ERROR(logger, "Reusing ADDR failed. Err: " << WSAGetLastError());
}
If socket is good
int result = bind(ls, reinterpret_cast<SOCKADDR*>(&server), sizeof(server));
where
server.sin_family = AF_INET;
server.sin_addr.s_addr = htonl(INADDR_ANY);
server.sin_port = htons(myport);
If bind succedes
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("e.f.g.h");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
if( setsockopt(ls, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*)&mreq, sizeof(mreq)) < 0 )
{
LOG4CXX_ERROR(logger, "setsockopt multicast group add membership failed. Err: " << WSAGetLastError());
}
The box on which I need to receive packets has four NICs, network administrators told me that I have to use the third one, let's say that it has a.b.c.d IPv4 address
They told me also that mcast IP is e.f.g.h
If I run windump.exe -i 3 on my windows box I see something like this
... 12:53:58.454987 IP i.k.l.m.xxxxx > e.f.g.h.myport: UDP, length 58
...
After initializing my UDP socket I call recvfrom
sz = recvfrom(ls, buffer, DATA_BLOCK_SIZE, 0, reinterpret_cast<SOCKADDR*>(&client), &size);
where sz is an int, ls is my socket, buffer is a "suitable buffer", DATA_BLOCK_SIZE is buffer size, client is a SOCKADDR pointer to receive info from the sender, and size is the received message size.
My code stucks in the recvfrom call never receiving anything.
I'm clearly making a mistake somewhere but not understanding where and worse why.
If someone can explain me what's happening it will be very appreciated.

SOLVED ...
I changed these lines only
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("e.f.g.h");
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
to
struct ip_mreq mreq;
inet_pton(AF_INET, "e.f.g.h", &(mreq.imr_multiaddr));
inet_pton(AF_INET, "a.b.c.d", &(mreq.imr_interface));
As I have guessed I was not correctly indicating in my struct ip_mreq which was the network interface to use for multicast messages.
It was my fault. Sorry for the noise.

Related

Can a UDP multicast socket be configured so that write() can be called rather than sendto()?

I am writing a C++ multicasting application on Linux Ubuntu.
In my C++ multicast sender class I do this:
uint16_t port = 5678;
const char* group = "239.128.128.128";
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(group);
addr.sin_port = htons(port);
const char* buf = "Hi there";
size_t bytes_to_write = 8;
size_t bytes_sent = sendto(fd, buf, bytes_to_write, 0, (struct sockaddr*) &addr, sizeof(addr));
Is there any way to configure the file descriptor so that I can call write() rather than sendto()? I would have thought there would be a setsockopt option or similar to do this?
Yes.
Per the documentation man 7 udp
When
connect(2) is called on the socket, the default destination address
is set and datagrams can now be sent using send(2) or write(2)
without specifying a destination address.
and, for generality, the POSIX spec for connect says
If the initiating socket is not connection-mode, then connect() shall set the socket's peer address, and no connection is made. For SOCK_DGRAM sockets, the peer address identifies where all datagrams are sent on subsequent send() functions, and limits the remote sender for subsequent recv() functions.
It's always worth checking the documentation for these, things, it isn't that impenetrable. FWIW I couldn't remember immediately whether you need connect() or bind() for this, and it took me a few seconds to find out.

Binding a socket to specific network

My computer is connected to multiple networks, say Network A and Network B.
I want to receive UDP broadcast packets from Network B only.
Currently, I am opening an UDP Socket and binding it to INADDR_ANY.
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( (unsigned short) 11000 );
if ( bind( handle, (const sockaddr*) &address, sizeof(sockaddr_in) ) < 0 )
{
//Operation Failed!
return false;
}
How to make sure that I will be receiving messages from Network B only?
The broadcast address is going to end with all-1 bits, so assuming "hoNetAddr" is a host-ordered address:
uint32_t hoBcastAddr = (hoNetAddr & hoNetMask) | (~hoNetMask);
// e.g. 192.168.0.0/255.255.255.0 =>
// (C0A80000 & FFFFFF00) | (/*inverted netmask*/ 000000FF)
// broadcast is: C0A800FF
address.sin_addr.s_addr = htonl(hoBcastAddr);
(assuming IPv4)

Cannot receive UDP Stream from Port 2368 (Linux) C

I'm currently working with a Laser Sensor that delivers a UDP data stream on Port 2368. I can see the packets with Wireshark.
As I'm not able to post an image, I write what Wireshark shows for a packet:
Source: 192.168.17.141
Destination: 192.168.3.255
Protocol: UDP
Source Port: https (443)
Destination Port: opentable (2368)
However, I want to read the packets using sockets with following example C program:
int main(int argc, char *argv[])
{
int sock, n, res;
unsigned int length = 1206;
char* buffer = new char[1206];
sock= socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) error("socket");
uint16_t udp_port = 2368;
sockaddr_in my_addr;
socklen_t len = sizeof(sockaddr_in);
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(udp_port);
my_addr.sin_addr.s_addr = INADDR_ANY;
cout << my_addr.sin_family << endl;
cout << my_addr.sin_port << endl;
cout << my_addr.sin_addr.s_addr << endl;
res = bind(sock, (sockaddr *)&my_addr, sizeof(sockaddr_in));
if (res == -1)
{
perror("bind");
return -1;
}
while (true)
{
n = recvfrom(sock,buffer,1,0,NULL,NULL);
if (n < 0) error("recvfrom");
}
close(sock);
return 0;
}
The program is successful until it comes to recvfrom(). There the socket waits for packages and does not receive anything. I wrote the same program for Windows with Winsock and it worked perfectly. As I am relatively new to Linux OS I do not know how to fix this problem and would be thankful for advice!
Additional information: I manually assigned following IP and netmask to eth4 (this is the interface where the device is connected):
IP: 192.168.3.5
NM: 255.255.255.0
Set the SO_BROADCAST option, even for receiving. According to the socket(7) manpage:
SO_BROADCAST:
Set or get the broadcast flag. When enabled, datagram sockets receive packets sent to a broadcast address and they are allowed to send packets to a broadcast address. This option has no effect on stream-oriented sockets.
It could also be that your interface config is incorrect. Verify that you have a 192.168.3.xxx/24 address configured for the interface in question.
char buffer[1200+6]; /* no need for dynamic buffers */
...
n = recvfrom(sock, buffer, sizeof buffer, 0, NULL, NULL);
BTW your usage of recvfrom() is equivalent to
n = recv(sock, buffer, sizeof buffer, 0);
, or even:
n = read(sock, buffer, sizeof buffer);
You have
IP: 192.168.3.5 NM: 255.255.255.0
and
Source: 192.168.17.141 Destination: 192.168.3.255
This can't work, if there is no router involved. Try
IP: 192.168.3.5 NM: 255.255.0.0
as an interims measure, but do read up on IP
Edit: Maybe better look into your Laser sensor and set it to 192.168.3.[something free] with Destination directly your 192.168.3.5, and debug the broadcasting later.

Socket transmit data but can't receive response

I am trying to implement UpNP in C++, I found a few sources on google but none worked. I found this one working (http://www.codeproject.com/KB/IP/upnplib.aspx) but it's for .NET, so I decided to sniff the network to see what the code was doing and then do the same with sockets.
Here are the results (full size: http://i.stack.imgur.com/eLoHK.jpg):
That shows me that the packet doesn't look bad, everything seems to be the same, everything but the source address of my code, which I don't know how to control (both my code and finder.net.exe are being tested on the same computer connected to the same network).
Here's my code:
#define upnp_broadcast_ip "239.255.255.250"
#define upnp_broadcast_port 1900
#define upnp_search_request "M-SEARCH * HTTP/1.1\r\n" \
"Host:239.255.255.250:1900\r\n" \
"ST:upnp:rootdevice\r\n" \
"Man:\"ssdp:discover\"\r\n" \
"MX:3\r\n" \
"\r\n"
WSAStartup(MAKEWORD(2, 2), &WsaData);
BOOL discover( )
{
SOCKET ConnectSocket;
struct sockaddr_in Addr;
char Buffer[1450];
int t = 0,
iResult = 0,
TrueLen = sizeof(bool);
bool True = true;
ulong One = 1;
// Open datagram socket
ConnectSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
// Clear out struct
memset( &Addr, 0, sizeof(Addr) );
// Specify the address family, IP address, and port
Addr.sin_family = AF_INET;
Addr.sin_port = htons( upnp_broadcast_port );
Addr.sin_addr.s_addr = inet_addr( upnp_broadcast_ip );
iResult = setsockopt( ConnectSocket, SOL_SOCKET, SO_BROADCAST, (char*)&True, TrueLen ); // Not sure what is this for
// Transmit data
int sent = sendto( ConnectSocket, upnp_search_request, strlen(upnp_search_request), 0, (struct sockaddr*)&Addr, sizeof(Addr) );
// Try to receive data 10 times
for( t = 0; t < 10; t++ )
{
ioctlsocket( ConnectSocket, FIONBIO, &One );
// Clear out buffer
memset( &Buffer, 0, sizeof(Buffer) );
int length = sizeof(Addr);
// Receive data
iResult = recvfrom( ConnectSocket, Buffer, (sizeof(Buffer) - 1), 0, (struct sockaddr*)&Addr, &length );
if( iResult == SOCKET_ERROR)
{
Sleep( 1000 );
continue;
} else {
// Do stuff with received data
}
}
closesocket( ConnectSocket );
return FALSE;
}
I removed all the WSAGetLastError() error checking to make the code easier to read, everything goes fine until recvfrom, that always returns -1 and strerror(WSAGetLastError()) prints "Unknown error".
I hope someone could guide me in the right direction, I've been the last two days trying to make this work.
Why broadcast? UPnP uses multicast, so as far as I remember (Unix) you should use setsockopt() to request that the kernel join a multicast group. I am not sure about Windows, it could be the same call.
Something like:
struct ip_mreq mreq;
....
mreq.imr_multiaddr.s_addr=inet_addr(GROUP_IP);
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))
EDIT:
As hasturkun pointed out, my initial answer was wrong. Also, slemdx correctly diagnosed the problem: the uPnP request is going out from the wrong interface. The problem is, I am not sure how you can determine the right interface. One possibility is to use the interface containing the default gateway on the routing table, but I don't think that would be the right choice. There may be uPnP devices hooked to other interfaces.
One option is to send the initial search packet on all available interfaces. Maybe the answers to this question can help. There is also another link on the last answer that you should check out.

Reopen connected datagram socket

I have a connection protocol that has been defined by our customer. Data are sent between two linux computers using UDP and TCP protocols. The IP addresses and ports are fixed on startup.
We are sending messages at 200 Hz and I have been using connect to save some time on the transmissions.
My problem is that if there is a communication error, I need to tear down the connections and reinitialise.
I have a problem with one of the UDP connections as it will not rebind to the required address and returns errno 22.
The code I am using is something like:
int
doConnect(int& sock, int local_port, char *local_ip, int remote_port, char *remote_ip)
{
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in);
addr.sin_family = AF_INET;
addr.sin_port = htons(local_port);
inet_pton(local_ip,&addr.sin_addr.s_addr);
if (0 > bind(sock, (struct sockaddr*)&addr, sizeof(addr)))
{
printf("Bind Error errno = %d\n", errno);
return ERR_BIND;
}
memset(&addr, 0, sizeof(sockaddr_in);
addr.sin_family = AF_INET;
addr.sin_port = htons(remote_port);
inet_pton(remote_ip,&addr.sin_addr.s_addr);
if (0 > connect(sock, (struct sockaddr*)&addr, sizeof(addr)))
{
printf("Connect Error errno = %d\n", errno);
return ERR_CONNECT;
}
return ERR_OK;
}
The way that this is used is like this:
int s1(-1), s2(-1);
doConnect(s1, 31003, "172.17.21.255", 31006, "172.17.21.1");
doConnect(s2, 31001, "172.17.21.3", 31004, "172.17.21.1");
When an error occurs
close(s1);
close(s2);
doConnect(s1, 31003, "172.17.21.255", 31006, "172.17.21.1");
doConnect(s2, 31001, "172.17.21.3", 31004, "172.17.21.1");
Here the local address is 172.17.21.3 and I am connecting to 172.17.21.1. s1 listens to a broadcast message.
s1 successfully reconnects to the remote machine, but s2 fails with error 22 from the call to bind.
I have tried explicitly calling bind and connect to an AF_UNSPEC address immediately before I close the socket. This doesn't solve the problem.
Are there any options that I should be using?
Perhaps you could try:
int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
I also suggest you double check that you're not passing the same socket to the two consecutive doConnect() calls (as errno 22 = EINVAL, which in the case of bind() appears to mean that the socket is already bound to an address).
The underlying socket layer might hold the port & IP address still open, even after your call to close. Try some of the following:
do a sleep(10) (or more) between the close and the call to doConnect again
configure the sockets using setsockopt with the SO_LINGER set to off
This actually happens more commonly with TCP connections, but I see no reason UDP can't have this problem as well.