According to the connect(2) man pages
If the socket sockfd is of type SOCK_DGRAM then serv_addr is the address to which datagrams are sent by default, and the only address from which datagrams are received. If the socket is of type SOCK_STREAM or SOCK_SEQPACKET, this call attempts to make a connection to the socket that is bound to the address specified by serv_addr.
I am trying to filter packets from two different multicast groups that are being broadcasted on the same port and I thought connect() would have done the job but I can't make it work. In facts when I add it to my program I don't receive any packet. More info in this thread.
This is how I set the connect parameters:
memset(&mc_addr, 0, sizeof(mc_addr));
mc_addr.sin_family = AF_INET;
mc_addr.sin_addr.s_addr = inet_addr(multicast_addr);
mc_addr.sin_port = htons(multicast_port);
printf("Connecting...\n");
if( connect(sd, (struct sockaddr*)&mc_addr, sizeof(mc_addr)) < 0 ) {
perror("connect");
return -1;
}
printf("Receiving...\n");
while( (len = recv(sd, msg_buf, sizeof(msg_buf), 0)) > 0 )
printf("Received %d bytes\n", len);
Unlike send(), the recv() function of Python's socket module can be used to receive data from both TCP and UDP sockets.
Datagram (UDP) sockets. The CONNECT command enables an application to associate a socket with the socket name of a peer. The socket then is considered to be a connected UDP socket. You can call the CONNECT command multiple times with different peer names to change the socket association.
The recv() function receives data on a socket with descriptor socket and stores it in a buffer. The recv() call applies only to connected sockets.
The only difference between recv() and read(2) is the presence of flags. With a zero flags argument, recv() is generally equivalent to read(2) (but see NOTES).
Your program (probably) has the following problems:
Here's an example program that receives multicasts. It uses recvfrom(), not recv(), but it's the same except you also get the source address for each received packet.
To receive from multiple multicast groups, you have three options.
First option: Use a separate socket for each multicast group, and bind() each socket to a multicast address. This is the simplest option.
Second option: Use a separate socket for each multicast group, bind() each socket INADDR_ANY, and use a socket filter to filter out all but a single multicast group.
Because you've bound to INADDR_ANY, you may still get packets for other multicast groups. It is possible to filter them out using the kernel's socket filters:
#include <stdint.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/filter.h>
/**
* Adds a Linux socket filter to a socket so that only IP
* packets with the given destination IP address will pass.
* dst_addr is in network byte order.
*/
int add_ip_dst_filter (int fd, uint32_t dst_addr)
{
uint16_t hi = ntohl(dst_addr) >> 16;
uint16_t lo = ntohl(dst_addr) & 0xFFFF;
struct sock_filter filter[] = {
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 16), // A <- IP dst high
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, hi, 0, 3), // if A != hi, goto ignore
BPF_STMT(BPF_LD + BPF_H + BPF_ABS, SKF_NET_OFF + 18), // A <- IP dst low
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, lo, 0, 1), // if A != lo, goto ignore
BPF_STMT(BPF_RET + BPF_K, 65535), // accept
BPF_STMT(BPF_RET + BPF_K, 0) // ignore
};
struct sock_fprog fprog = {
.len = sizeof(filter) / sizeof(filter[0]),
.filter = filter
};
return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
}
Third option: use a single socket to receive multicasts for all multicast groups.
In that case, you should do an IP_ADD_MEMBERSHIP for each of the groups. This way you get all packets on a single socket.
However, you need extra code to determine which multicast group a received packet was addressed to. To do that, you have to:
The exact thing you need to do depends on IP protocol version and OS. Here's how I did it (IPv6 code not tested): enabling PKTINFO and reading the option.
Here's a simple program that receives multicasts, which demonstrates the first option (bind to multicast address).
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MAXBUFSIZE 65536
int main (int argc, char **argv)
{
if (argc != 4) {
printf("Usage: %s <group address> <port> <interface address>\n", argv[0]);
return 1;
}
int sock, status, socklen;
char buffer[MAXBUFSIZE+1];
struct sockaddr_in saddr;
struct ip_mreq imreq;
// set content of struct saddr and imreq to zero
memset(&saddr, 0, sizeof(struct sockaddr_in));
memset(&imreq, 0, sizeof(struct ip_mreq));
// open a UDP socket
sock = socket(PF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket failed!");
return 1;
}
// join group
imreq.imr_multiaddr.s_addr = inet_addr(argv[1]);
imreq.imr_interface.s_addr = inet_addr(argv[3]);
status = setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(const void *)&imreq, sizeof(struct ip_mreq));
saddr.sin_family = PF_INET;
saddr.sin_port = htons(atoi(argv[2]));
saddr.sin_addr.s_addr = inet_addr(argv[1]);
status = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (status < 0) {
perror("bind failed!");
return 1;
}
// receive packets from socket
while (1) {
socklen = sizeof(saddr);
status = recvfrom(sock, buffer, MAXBUFSIZE, 0, (struct sockaddr *)&saddr, &socklen);
if (status < 0) {
printf("recvfrom failed!\n");
return 1;
}
buffer[status] = '\0';
printf("Received: '%s'\n", buffer);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With