Reputation: 136
I writting an application that should be able to receive IPv4 or IPv6 multicast datagrams on a socket. I wrote a function that enables the reception of multicast datagrams for a socket through setsockopt
(see code below). The strange problem that i am having is that the setsockopt for the IPv4 case IP_ADD_MEMBERSHIP
is sometimes failing with the errno No such device
and the other times it is working as expected. My application is running on a raspberry pi with raspbian. I really appreciate your suggestions!
void setRecvMulticastAddr(int *socketFD, struct sockaddr *addr, char *interface, char *multicastaddr){
// Cast the sockaddr to a sockaddr storage struct
struct sockaddr_storage *addrStorage = (struct sockaddr_storage *) addr;
// Check if it is an IPv4 or IPv6 socket
if(addrStorage->ss_family == AF_INET){
// IPv4 multicast request
struct ip_mreq mreq;
// Convert the multicast IPv4 address string to an in_addr
struct in_addr multiaddr;
if(inet_pton(AF_INET, multicastaddr, &multiaddr) != 1){
printf("Could not convert the IPv4 multicast address: %s", multicastaddr);
exit(ERR_INETPTON_FAILED);
}
// Cast the sockaddr to a sockaddr_in struct
struct sockaddr_in *addrin = (struct sockaddr_in *) addrStorage;
// Fill out the IPv4 multicast request
mreq.imr_interface = addrin->sin_addr;
mreq.imr_multiaddr = multiaddr;
// ### The setsockopt that fails sometimes: ###
// Set the sockopt so the socket can receive on the multicast address
if(setsockopt(*socketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) != 0){
perror("Error setsockopt IP_ADD_MEMBERSHIP failed");
exit(ERR_SETSOCKOPT_FAILED);
}
}else{
// IPv6 multicast request
struct ipv6_mreq mreq;
// Set the interace name in the ifr
struct ifreq ifr;
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface);
// Get the ifrindex based on the interface name
if(ioctl(*socketFD, SIOGIFINDEX, &ifr) < 0){
printf("Could not get ifrindex: %s\n", strerror(errno));
}
// Convert the multicast addrress string to an in_addr6
struct in6_addr multiaddr;
if(inet_pton(AF_INET6, multicastaddr, &multiaddr) != 1){
printf("Could not convert the IPv6 multicast address: %s", multicastaddr);
exit(ERR_INETPTON_FAILED);
}
// Fill out the IPv6 multicast request
mreq.ipv6mr_interface = ifr.ifr_ifindex;
mreq.ipv6mr_multiaddr = multiaddr;
// Set the sockopt so the socket can receive on the multicast address
if(setsockopt(*socketFD, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) != 0){
perror("Error setsockopt IPV6_JOIN_GROUP failed");
exit(ERR_SETSOCKOPT_FAILED);
}
}
return;
}
Upvotes: 1
Views: 2209
Reputation: 136
Based on the discussion with @dbush i hopefully improved the code by:
Here is the new code for the IPv4 case:
// IPv4 multicast request
struct ip_mreqn mreqn;
// Set the interace family and name in the ifr
struct ifreq ifr;
ifr.ifr_addr.sa_family = AF_INET;
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", interface);
// Get the ifrindex of the interface
if(ioctl(*socketFD, SIOGIFINDEX, &ifr) < 0){
printf("Error could not get the interface index: %s\n", strerror(errno));
close(*socketFD);
exit(ERR_IOCTL_INDEX);
}
mreqn.imr_ifindex = ifr.ifr_ifindex;
// Get the IP address of the interface
if(ioctl(*socketFD, SIOCGIFADDR, &ifr) != 0){
printf("Error could not get the interface address: %s\n", strerror(errno));
close(*socketFD);
exit(ERR_IOCTL_ADDR);
}
// Cast the sockaddr to a sockaddr_in to get the in_addr value
struct sockaddr_in *ifaddr = (struct sockaddr_in*) &ifr.ifr_addr;
mreqn.imr_address = ifaddr->sin_addr;
// Convert the multicast IPv4 address string to an in_addr
struct in_addr multiaddr;
if(inet_pton(AF_INET, multicastaddr, &multiaddr) != 1){
printf("Error could not convert the IPv4 multicast address: %s", multicastaddr);
exit(ERR_INETPTON_FAILED);
}
mreqn.imr_multiaddr = multiaddr;
// Set the sockopt so the socket can receive on the multicast address
if(setsockopt(*socketFD, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn)) != 0){
perror("Error setsockopt IP_ADD_MEMBERSHIP failed");
exit(ERR_SETSOCKOPT_FAILED);
}
Upvotes: 1